java - Comparaison des performances de concaténation de cinq chaînes à l'aide du framework d'analyse comparative JMH
introduction
Java propose 5 méthodes de concaténation de chaînes, et l'utilisation de + pour concaténer des chaînes est la méthode la plus courante. De plus, il existe StringBuilder, StringBuffer, MessageFormat et StringFormat
pour comparer les performances des cinq méthodes suivantes simplement à partir du temps d'exécution de l'épissage.
Comparaison des résultats
Regardez d'abord les résultats de l'exécution, où Score est le temps d'exécution (subtil) et Error peut être considéré comme une erreur.
Le temps d'exécution du court au long est : + < StringBuilder < StringBuilder < MessageFormat < StringFormat
Benchmark Mode Cnt Score Error Units
JmhTest.testMessageFormat avgt 5 722.346 ± 134.540 us/op
JmhTest.testStringBase avgt 5 6.905 ± 2.604 us/op
JmhTest.testStringBuffer avgt 5 8.291 ± 5.311 us/op
JmhTest.testStringBuilder avgt 5 7.192 ± 3.861 us/op
JmhTest.testStringFormat avgt 5 1273.906 ± 69.336 us/op
référence jmh
ajouter des dépendances
<!--基准测试-->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.35</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.35</version>
</dependency>
Remarques JMH
@BenchMarkMode Définir le mode de test de référence [méthode ou classe]
@OutPutTimeUnit L'unité de temps par défaut pour rapporter les résultats [classe, méthode]
@Warmup warmup, définir des paramètres de configuration spécifiques tels que les heures, l'heure, etc.
@Measurement est similaire à l'échauffement, mais le paramètre est @Fork lors de la mesure
du test global plusieurs fois
@State définir la portée des objets de configuration, définir le degré de partage entre les threads
@Setup fonction de configuration du thread avant l'exécution, initialisation
@TearDown opérations de traitement post-test 【Méthode】
@BenchMark mark test benchmark [Méthode]
@OperationsPerInvocation effectue une communication multi-opérations avec le benchmark et exécute l'ajustement JMH
- @BenchMarkMode
définit le mode d'exécution du test de référence. Vous pouvez choisir de le mettre sur la méthode et de ne prendre effet que pour la méthode.
Mode.Throughput : mode Débit, obtenez le nombre d'opérations par unité de temps, exécutez la méthode @BenchMark en continu et calculez le débit total de tous les threads de travail.
Mode.AverageTime : mode Temps moyen, obtenez le temps moyen de chaque opération et calculez le temps moyen de tous les threads de travail.
Mode.SimpleTime : mode d'échantillonnage temporel, qui échantillonne l'heure de chaque fonction d'opération, exécute les fonctions @BenchMark en continu et extrait de manière aléatoire le temps requis pour l'opération.
Mode.SingleShotTime : mode de déclenchement unique, testez le temps d'une seule opération, exécutez la fonction @BenchMark en continu, exécutez-la une seule fois et calculez le temps : ce mode n'exécute la fonction @BenchMark qu'une seule fois, elle doit donc se réchauffer, si la valeur de référence est petite, utilisez l'échantillonnage SimpleTime Pattern.
Mode.All : pas de mode, utilisez tous les modes de base, le meilleur effet.
écrire le code de test
Exécutez la méthode principale pour démarrer le test et attendez patiemment que le test soit terminé.
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.text.MessageFormat;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime) //基准测试的默认模式
@OutputTimeUnit(TimeUnit.MICROSECONDS) //时间单位:纳秒、微妙、毫秒、秒、分、时
@State(Scope.Thread)
@Fork(1) //进程数一般设置为1
//@Threads(1) //线程数
@Warmup(iterations = 2,time = 2) //预热迭代次数,time控制每次迭代的间隔时间(默认秒)
@Measurement(iterations = 5,time = 2) //测量迭代次数,time控制每次迭代的间隔时间(默认秒)
public class JmhTest {
private int _loop = 1000;
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(JmhTest.class.getSimpleName()).build();
new Runner(opt).run();
}
//StringBuffer
@Benchmark //被测试的方法
public void testStringBuffer() {
for(int i = 0; i< _loop; i++){
StringBuffer sbr = new StringBuffer();
String s = sbr.append("1111").append(",")
.append("2222").append(",")
.append("3333").append(",")
.append("4444").toString();
}
}
//StringBuilder
@Benchmark //被测试的方法
public void testStringBuilder() {
for(int i = 0; i< _loop; i++){
StringBuilder sbr = new StringBuilder();
String s = sbr.append("1111").append(",")
.append("2222").append(",")
.append("3333").append(",")
.append("4444").toString();
}
}
//MessageFormat
@Benchmark
public void testMessageFormat() {
for(int i = 0; i< _loop; i++){
String s = MessageFormat.format("{0},{1},{2},{3}","1111","2222","3333","4444");
}
}
//String.format
@Benchmark
public void testStringFormat() {
for(int i = 0; i< _loop; i++){
String s = String.format("%s,%s,%s,%s","1111","2222","3333","4444");
}
}
//字符串拼接
@Benchmark
public void testStringBase() {
for(int i = 0; i< _loop; i++){
String s = "1111";
s+=",2222";
s+=",3333";
s+=",4444";
}
}
}
Description des résultats des tests
Ce qui précède évalue en réalité 5 fonctions. La configuration du test est que chaque fonction est préchauffée et exécutée une fois, et le temps d'exécution moyen des 5 fois suivantes est compté.
True pour utiliser le test de mode Mode.AverageTime, la sortie après l'exécution de chaque fonction est :
Result "com.cnpc.epai.researchdata.data.service.JmhTest.testStringFormat":
1254.609 ±(99.9%) 174.543 us/op [Average]
(min, avg, max) = (1174.821, 1254.609, 1282.413), stdev = 45.328
CI (99.9%): [1080.066, 1429.151] (assumes normal distribution)
Le résultat final de l'exécution est :
Benchmark Mode Cnt Score Error Units
JmhTest.testMessageFormat avgt 5 722.346 ± 134.540 us/op
JmhTest.testStringBase avgt 5 6.905 ± 2.604 us/op
JmhTest.testStringBuffer avgt 5 8.291 ± 5.311 us/op
JmhTest.testStringBuilder avgt 5 7.192 ± 3.861 us/op
JmhTest.testStringFormat avgt 5 1273.906 ± 69.336 us/op
Où Score est le temps d'exécution (subtil) et Error est l'erreur ;
en conclusion:
1. La manière d’utiliser StringBuilder est la plus efficace.
2. Si vous n'effectuez pas d'épissage de chaîne dans le corps de la boucle, utilisez simplement + directement.
3. Si l'épissage de chaînes est effectué dans un scénario simultané, StringBuffer doit être utilisé à la place de StringBuilder.