Има няколко точки, които трябва да имате предвид, преди да напишете такъв бенчмарк (и особено бенчмарк, използващ JVM):
-
на повечето (физически) машини Redis е в състояние да обработва повече от 100K ops/s, когато се използва конвейер. Вашият бенчмарк се занимава само със 100K артикула, така че не трае достатъчно дълго, за да даде значими резултати. Освен това няма време за започване на последователните етапи на JIT.
-
абсолютното време не е много уместен показател. Показването на пропускателната способност (т.е. броя на операциите в секунда), като същевременно поддържането на еталонния тест за поне 10 секунди, би било по-добър и по-стабилен показател.
-
вашият вътрешен цикъл генерира много боклук. Ако планирате да сравните Jedis+Redis, тогава трябва да поддържате режийните разходи за собствената си програма ниски.
-
тъй като сте дефинирали всичко в основната функция, вашият цикъл няма да бъде компилиран от JIT (в зависимост от JVM, който използвате). Само вътрешните извиквания на метод могат да бъдат. Ако искате JIT да бъде ефективен, не забравяйте да капсулирате кода си в методи, които могат да бъдат компилирани от JIT.
-
по избор може да искате да добавите фаза на загряване, преди да извършите действителното измерване, за да избегнете отчитането на режийните разходи за изпълнение на първите итерации с основния интерпретатор и разходите за самия JIT.
Сега, по отношение на тръбопровода Redis, вашият тръбопровод е твърде дълъг. 100K команди в тръбопровода означават, че Jedis трябва да изградят 6MB буфер, преди да изпратят каквото и да било до Redis. Това означава, че буферите на сокета (от страна на клиента и може би от страна на сървъра) ще бъдат наситени и че Redis ще трябва да се справи и с 6 MB комуникационни буфери.
Освен това вашият бенчмарк все още е синхронен (използването на конвейер не го прави магически асинхронен). С други думи, джедаите няма да започнат да четат отговори, докато последната заявка от вашия конвейер не бъде изпратена до Redis. Когато тръбопроводът е твърде дълъг, има потенциал да блокира нещата.
Помислете за ограничаване на размера на тръбопровода до 100-1000 операции. Разбира се, това ще генерира повече двупосочни пътувания, но натискът върху комуникационния стек ще бъде намален до приемливо ниво. Например, помислете за следната програма:
import redis.clients.jedis.*;
import java.util.*;
public class TestPipeline {
/**
* @param args
*/
int i = 0;
Map<String, String> map = new HashMap<String, String>();
ShardedJedis jedis;
// Number of iterations
// Use 1000 to test with the pipeline, 100 otherwise
static final int N = 1000;
public TestPipeline() {
JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379);
List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
list.add(si);
jedis = new ShardedJedis(list);
}
public void push( int n ) {
ShardedJedisPipeline pipeline = jedis.pipelined();
for ( int k = 0; k < n; k++) {
map.put("id", "" + i);
map.put("name", "lyj" + i);
pipeline.hmset("m" + i, map);
++i;
}
pipeline.sync();
}
public void push2( int n ) {
for ( int k = 0; k < n; k++) {
map.put("id", "" + i);
map.put("name", "lyj" + i);
jedis.hmset("m" + i, map);
++i;
}
}
public static void main(String[] args) {
TestPipeline obj = new TestPipeline();
long startTime = System.currentTimeMillis();
for ( int j=0; j<N; j++ ) {
// Use push2 instead to test without pipeline
obj.push(1000);
// Uncomment to see the acceleration
//System.out.println(obj.i);
}
long endTime = System.currentTimeMillis();
double d = 1000.0 * obj.i;
d /= (double)(endTime - startTime);
System.out.println("Throughput: "+d);
}
}
С тази програма можете да тествате със или без конвейер. Уверете се, че сте увеличили броя на повторенията (параметър N), когато се използва конвейер, така че да работи поне 10 секунди. Ако декомментирате println в цикъла, ще разберете, че програмата е бавна в началото и ще става по-бърза, когато JIT започне да оптимизира нещата (ето защо програмата трябва да работи поне няколко секунди, за да даде смислен резултат).
На моя хардуер (стара кутия на Athlon) мога да получа 8-9 пъти повече пропускателна способност, когато се използва конвейерът. Програмата може да бъде допълнително подобрена чрез оптимизиране на форматирането ключ/стойност във вътрешния цикъл и добавяне на фаза на загряване.