Det finns några punkter du måste tänka på innan du skriver ett sådant riktmärke (och särskilt ett riktmärke med JVM):
-
på de flesta (fysiska) maskiner kan Redis bearbeta mer än 100K ops/s när pipelining används. Ditt riktmärke handlar bara om 100 000 föremål, så det varar inte tillräckligt länge för att ge meningsfulla resultat. Dessutom finns det ingen tid för de successiva stadierna av JIT att starta.
-
den absoluta tiden är inte ett särskilt relevant mått. Att visa genomströmningen (dvs. antalet operationer per sekund) samtidigt som riktmärket körs i minst 10 sekunder skulle vara ett bättre och mer stabilt mått.
-
din inre loop genererar mycket skräp. Om du planerar att benchmarka Jedis+Redis, måste du hålla omkostnaden för ditt eget program låg.
-
eftersom du har definierat allt i huvudfunktionen kommer din loop inte att kompileras av JIT (beroende på vilken JVM du använder). Endast de inre metodanropen får vara. Om du vill att JIT ska vara effektiv, se till att kapsla in din kod i metoder som kan kompileras av JIT.
-
eventuellt kan du lägga till en uppvärmningsfas innan du utför den faktiska mätningen för att undvika att ta hänsyn till omkostnaderna för att köra de första iterationerna med bara-benstolken och kostnaden för själva JIT.
Nu när det gäller Redis pipelining är din pipeline alldeles för lång. 100K kommandon i pipeline innebär att Jedis måste bygga en 6MB buffert innan något skickas till Redis. Det betyder att socketbuffertarna (på klientsidan och kanske serversidan) kommer att vara mättade och att Redis också kommer att behöva hantera 6 MB kommunikationsbuffertar.
Dessutom är ditt riktmärke fortfarande synkront (att använda en pipeline gör den inte magiskt asynkron). Med andra ord kommer Jedis inte att börja läsa svar förrän den sista frågan i din pipeline har skickats till Redis. När pipelinen är för lång har den potential att blockera saker.
Överväg att begränsa storleken på rörledningen till 100-1000 operationer. Naturligtvis kommer det att generera fler rundresor, men trycket på kommunikationsstacken kommer att minska till en acceptabel nivå. Tänk till exempel på följande program:
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);
}
}
Med detta program kan du testa med eller utan pipelining. Se till att öka antalet iterationer (N-parameter) när pipelining används, så att den körs i minst 10 sekunder. Om du avkommenterar println i slingan kommer du att inse att programmet är långsamt i början och kommer att bli snabbare när JIT börjar optimera saker (det är därför programmet bör köras minst flera sekunder för att ge ett meningsfullt resultat).
På min hårdvara (en gammal Athlon-box) kan jag få 8-9 gånger mer genomströmning när pipelinen används. Programmet skulle kunna förbättras ytterligare genom att optimera nyckel-/värdeformateringen i den inre slingan och lägga till en uppvärmningsfas.