2015-05-21 15 views
7

Tôi đã gặp phải sự cố lạ trong sản xuất hiện nay. Mặc dù tôi yêu ổi, tôi đã gặp phải một trường hợp sử dụng khi số Sets.intersection() của Guava diễn ra khá tệ. Tôi đã viết một mã mẫu:Hiệu suất không ổn định của Bộ hiệu ứng ổi

Set<Long> cache = new HashSet<>(); 
for (long i = 0; i < 1000000; i++) { 
    cache.add(i); 
} 
Set<Long> keys = new HashSet<>(); 
for (long i = 0; i < 100; i++) { 
    keys.add(i); 
} 
long start = System.currentTimeMillis(); 
Set<Long> foundKeys = new HashSet<>(); 
for (Long key : keys) { 
    if (cache.contains(key)) { 
     foundKeys.add(key); 
    } 
} 
System.out.println("Java search: " + (System.currentTimeMillis() - start)); 
start = System.currentTimeMillis(); 
SetView<Long> intersection = Sets.intersection(keys, cache); 
System.out.println("Guava search: " + (System.currentTimeMillis() - start)); 

Tôi đã cố gắng tạo ra một kịch bản sản xuất tương tự khi tôi có bộ đệm khóa và tôi đang tìm tất cả các khóa có trong bộ nhớ cache. Kỳ lạ thay, tìm kiếm ổi biến mất nhiều thời gian hơn tìm kiếm Java. Sau khi chạy này tôi nhận:

Java search: 0 
Guava search: 36 

bất cứ ai có thể cho lý do tại sao điều này là không phù hợp đối với trường hợp sử dụng của tôi hoặc là có một lỗi trong ổi?

+1

vui lòng xem http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java –

+0

Có, việc triển khai Ổi thực hiện không đối xứng: nếu tập đầu tiên lớn hơn rất nhiều so với tập thứ hai, nó sẽ chậm hơn rất nhiều. Thử chuyển đổi hai bộ. – biziclop

+0

Có, tập đầu tiên của tôi đã nhỏ hơn nhiều. – Heisenberg

Trả lời

7

Hóa ra sự cố là nhiều cuộc gọi đến SetView.size(). Vì SetView là chế độ xem (trực tiếp) của giao điểm của hai tập hợp, kích thước giao lộ cần phải được tính toán lại mỗi lần.

public static <E> SetView<E> intersection(final Set<E> set1, final Set<?> set2) { 
//... 
    return new SetView<E>() { 
    @Override public Iterator<E> iterator() { 
     return Iterators.filter(set1.iterator(), inSet2); 
    } 
    @Override public int size() { 
     return Iterators.size(iterator()); 
    } 
    //... 
    }; 
} 

Như có thể thấy ở đây, những gì tính toán lại có nghĩa là trong trường hợp này đang lặp qua toàn bộ chế độ xem, có thể mất nhiều thời gian.

Cách này để đảm bảo size() chỉ được gọi một lần và giá trị được lưu trữ (nếu bạn biết các bộ cơ bản sẽ không thay đổi) hoặc nếu không thể, hãy tạo bản sao của giao lộ qua ImmutableSet.copyOf() (ví dụ).

+0

Nếu tập đầu tiên chúng tôi vượt qua lớn hơn, thì chỉ có chúng tôi gặp sự cố với kích thước(), nếu không nó có vẻ hoạt động tốt. – Heisenberg

+2

Lưu ý rằng 'SetView' có một phương thức' immutableCopy() 'trả về một' ImmutableSet'. – ColinD

+0

@ColinD Hmm, tôi không biết điều đó, có vẻ hữu ích. Cảm ơn vì tiền hỗ trợ. – biziclop