Có một số điểm bạn cần cân nhắc trước khi viết điểm chuẩn như vậy (và đặc biệt là điểm chuẩn sử dụng JVM):
-
trên hầu hết các máy (vật lý), Redis có thể xử lý hơn 100K ops / s khi sử dụng pipelining. Điểm chuẩn của bạn chỉ giao dịch với mặt hàng 100K, vì vậy nó không kéo dài đủ lâu để tạo ra kết quả có ý nghĩa. Hơn nữa, không có thời gian để các giai đoạn kế tiếp của JIT bắt đầu.
-
thời gian tuyệt đối không phải là một số liệu phù hợp lắm. Hiển thị thông lượng (tức là số lần hoạt động mỗi giây) trong khi giữ cho điểm chuẩn chạy trong ít nhất 10 giây sẽ là một số liệu tốt hơn và ổn định hơn.
-
vòng lặp bên trong của bạn tạo ra rất nhiều rác. Nếu bạn định lấy điểm chuẩn cho Jedis + Redis, thì bạn cần giữ chi phí cho chương trình của riêng mình ở mức thấp.
-
bởi vì bạn đã định nghĩa mọi thứ vào hàm chính, vòng lặp của bạn sẽ không được biên dịch bởi JIT (tùy thuộc vào JVM bạn sử dụng). Chỉ có thể gọi phương thức bên trong. Nếu bạn muốn JIT hoạt động hiệu quả, hãy đảm bảo đóng gói mã của bạn thành các phương thức mà JIT có thể biên dịch được.
-
tùy chọn, bạn có thể muốn thêm giai đoạn khởi động trước khi thực hiện phép đo thực tế để tránh tính chi phí chạy các lần lặp đầu tiên với trình thông dịch đơn giản và chi phí của chính JIT.
Bây giờ, liên quan đến đường ống của Redis, đường ống của bạn quá dài. 100K lệnh trong đường dẫn có nghĩa là Jedis phải xây dựng bộ đệm 6MB trước khi gửi bất cứ thứ gì đến Redis. Điều đó có nghĩa là các bộ đệm socket (ở phía máy khách và có lẽ là phía máy chủ) sẽ bị bão hòa và Redis cũng sẽ phải xử lý các bộ đệm giao tiếp 6 MB.
Hơn nữa, điểm chuẩn của bạn vẫn đồng bộ (việc sử dụng đường ống không làm cho nó không đồng bộ một cách kỳ diệu). Nói cách khác, Jedis sẽ không bắt đầu đọc các câu trả lời cho đến khi truy vấn cuối cùng trong đường dẫn của bạn được gửi đến Redis. Khi đường ống quá dài, nó có khả năng gây tắc nghẽn.
Xem xét giới hạn kích thước của đường ống trong 100-1000 lần hoạt động. Tất nhiên, nó sẽ tạo ra nhiều roundtrips hơn, nhưng áp lực lên ngăn xếp truyền thông sẽ giảm xuống mức có thể chấp nhận được. Ví dụ:hãy xem xét chương trình sau:
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);
}
}
Với chương trình này, bạn có thể kiểm tra có hoặc không có pipelining. Đảm bảo tăng số lần lặp (tham số N) khi sử dụng pipelining để nó chạy trong ít nhất 10 giây. Nếu bạn bỏ ghi chú println trong vòng lặp, bạn sẽ nhận ra rằng chương trình chạy chậm ngay từ đầu và sẽ nhanh hơn khi JIT bắt đầu tối ưu hóa mọi thứ (đó là lý do tại sao chương trình phải chạy ít nhất vài giây để đưa ra kết quả có ý nghĩa).
Trên phần cứng của tôi (một hộp Athlon cũ), tôi có thể nhận được thông lượng gấp 8-9 lần khi đường ống được sử dụng. Chương trình có thể được cải thiện hơn nữa bằng cách tối ưu hóa định dạng khóa / giá trị trong vòng lặp bên trong và thêm giai đoạn khởi động.