Jest kilka punktów, które należy wziąć pod uwagę przed napisaniem takiego testu porównawczego (a zwłaszcza testu porównawczego z użyciem JVM):
-
na większości (fizycznych) maszyn Redis jest w stanie przetworzyć ponad 100 000 operacji na sekundę, gdy używane jest potokowanie. Twój test porównawczy dotyczy tylko pozycji 100 000, więc nie trwa wystarczająco długo, aby uzyskać znaczące wyniki. Co więcej, nie ma czasu na rozpoczęcie kolejnych etapów JIT.
-
czas bezwzględny nie jest bardzo istotnym wskaźnikiem. Wyświetlanie przepustowości (tj. liczby operacji na sekundę) przy jednoczesnym utrzymaniu działania testu porównawczego przez co najmniej 10 sekund byłoby lepszym i bardziej stabilnym wskaźnikiem.
-
twoja wewnętrzna pętla generuje dużo śmieci. Jeśli planujesz przetestować Jedis+Redis, musisz utrzymać niskie koszty własnego programu.
-
ponieważ wszystko zdefiniowałeś w funkcji main, twoja pętla nie zostanie skompilowana przez JIT (w zależności od używanej maszyny JVM). Mogą być tylko wywołania metod wewnętrznych. Jeśli chcesz, aby JIT był wydajny, upewnij się, że Twój kod jest zamknięty w metodach, które mogą być skompilowane przez JIT.
-
opcjonalnie możesz chcieć dodać fazę rozgrzewania przed wykonaniem rzeczywistego pomiaru, aby uniknąć rozliczania kosztów związanych z uruchomieniem pierwszych iteracji za pomocą prostego interpretera oraz kosztów samego JIT.
Teraz, jeśli chodzi o potokowanie Redis, twój potok jest zbyt długi. 100 000 poleceń w potoku oznacza, że Jedis musi zbudować bufor o wielkości 6 MB przed wysłaniem czegokolwiek do Redis. Oznacza to, że bufory gniazd (po stronie klienta i być może po stronie serwera) zostaną nasycone, a Redis będzie musiał również poradzić sobie z buforami komunikacyjnymi o wielkości 6 MB.
Co więcej, twój test porównawczy jest nadal synchroniczny (używanie potoku nie czyni go w magiczny sposób asynchronicznym). Innymi słowy, Jedis nie zacznie czytać odpowiedzi, dopóki ostatnie zapytanie z potoku nie zostanie wysłane do Redis. Gdy potok jest zbyt długi, może to blokować.
Rozważ ograniczenie rozmiaru potoku do 100-1000 operacji. Oczywiście wygeneruje to więcej objazdów, ale nacisk na stos komunikacyjny zostanie zmniejszony do akceptowalnego poziomu. Rozważmy na przykład następujący 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);
}
}
Za pomocą tego programu możesz testować z potokowaniem lub bez. Pamiętaj, aby zwiększyć liczbę iteracji (parametr N), gdy używane jest potokowanie, tak aby działało przez co najmniej 10 sekund. Jeśli odkomentujesz println w pętli, zdasz sobie sprawę, że program jest powolny na początku i będzie szybszy, gdy JIT zacznie optymalizować rzeczy (dlatego program powinien działać przez co najmniej kilka sekund, aby dać sensowny wynik).
Na moim sprzęcie (stare pudełko Athlona) mogę uzyskać 8-9 razy większą przepustowość, gdy używany jest potok. Program można jeszcze ulepszyć, optymalizując formatowanie klucz/wartość w wewnętrznej pętli i dodając fazę rozgrzewki.