Chúng tôi đang phát triển một phần mềm quan trọng hiệu suất cao trong C ++. Ở đó chúng ta cần một bản đồ băm đồng thời và thực hiện một bản đồ. Vì vậy, chúng tôi đã viết một điểm chuẩn để tìm hiểu xem so sánh với bản đồ băm đồng thời của chúng tôi chậm hơn bao nhiêu std::unordered_map
.
Tuy nhiên, std::unordered_map
có vẻ như là cực kỳ chậm ... Vì vậy, đây là điểm chuẩn vi mô của chúng tôi (đối với bản đồ đồng thời, chúng tôi tạo ra một chuỗi mới để đảm bảo rằng khóa không bị tối ưu hóa và lưu ý rằng tôi không bao giờ chèn 0 vì tôi cũng đánh giá điểm chuẩn với google::dense_hash_map
, cần giá trị null):
boost::random::mt19937 rng;
boost::random::uniform_int_distribution<> dist(std::numeric_limits<uint64_t>::min(), std::numeric_limits<uint64_t>::max());
std::vector<uint64_t> vec(SIZE);
for (int i = 0; i < SIZE; ++i) {
uint64_t val = 0;
while (val == 0) {
val = dist(rng);
}
vec[i] = val;
}
std::unordered_map<int, long double> map;
auto begin = std::chrono::high_resolution_clock::now();
for (int i = 0; i < SIZE; ++i) {
map[vec[i]] = 0.0;
}
auto end = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
std::cout << "inserts: " << elapsed.count() << std::endl;
std::random_shuffle(vec.begin(), vec.end());
begin = std::chrono::high_resolution_clock::now();
long double val;
for (int i = 0; i < SIZE; ++i) {
val = map[vec[i]];
}
end = std::chrono::high_resolution_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin);
std::cout << "get: " << elapsed.count() << std::endl;
(CHỈNH SỬA: toàn bộ mã nguồn có thể được tìm thấy ở đây: http://pastebin.com/vPqf7eya )
Kết quả cho std::unordered_map
là:
inserts: 35126
get : 2959
Đối với google::dense_map
:
inserts: 3653
get : 816
Đối với bản đồ đồng thời được hỗ trợ bởi tay của chúng tôi (có khóa, mặc dù điểm chuẩn là một luồng - nhưng trong một luồng sinh sản riêng biệt):
inserts: 5213
get : 2594
Nếu tôi biên dịch chương trình điểm chuẩn mà không hỗ trợ pthread và chạy mọi thứ trong chuỗi chính, tôi nhận được kết quả sau cho bản đồ đồng thời được hỗ trợ bằng tay của chúng tôi:
inserts: 4441
get : 1180
Tôi biên dịch bằng lệnh sau:
g++-4.7 -O3 -DNDEBUG -I/tmp/benchmap/sparsehash-2.0.2/src/ -std=c++11 -pthread main.cc
Vì vậy, đặc biệt là các lượt chèn trên std::unordered_map
dường như cực kỳ tốn kém - 35 giây so với 3-5 giây cho các bản đồ khác. Ngoài ra thời gian tra cứu có vẻ khá cao.
Câu hỏi của tôi: tại sao lại như vậy? Tôi đọc một câu hỏi khác trên stackoverflow nơi ai đó hỏi, tại sao std::tr1::unordered_map
lại chậm hơn việc triển khai của chính anh ta. Ở đó, câu trả lời được xếp hạng cao nhất nói rằng std::tr1::unordered_map
cần phải triển khai một giao diện phức tạp hơn. Nhưng tôi không thể thấy đối số này: chúng tôi sử dụng phương pháp tiếp cận xô trong concurrent_map của chúng tôi, std::unordered_map
sử dụng cả phương pháp tiếp cận xô ( google::dense_hash_map
không, nhưng hơnstd::unordered_map
ít nhất phải nhanh hơn phiên bản an toàn đồng thời được hỗ trợ bởi tay của chúng tôi?). Ngoài điều đó ra, tôi không thể nhìn thấy bất kỳ thứ gì trong giao diện buộc một tính năng khiến bản đồ băm hoạt động không tốt ...
Vì vậy, câu hỏi của tôi: có đúng là nó std::unordered_map
có vẻ là rất chậm? Nếu không: điều gì là sai? Nếu có: lý do cho điều đó là gì.
Và câu hỏi chính của tôi: tại sao việc chèn một giá trị vào một giá trị std::unordered_map
quá đắt đỏ (ngay cả khi chúng ta đặt đủ dung lượng lúc đầu, nó không hoạt động tốt hơn nhiều - vì vậy việc tạo lại có vẻ không phải là vấn đề)?
BIÊN TẬP:
Trước hết: vâng, điểm chuẩn được trình bày không hoàn hảo - điều này là do chúng tôi đã chơi rất nhiều với nó và nó chỉ là một vụ hack (ví dụ: uint64
phân phối để tạo int trong thực tế không phải là một ý tưởng hay, loại trừ 0 trong vòng lặp là loại ngu ngốc vv ...).
Hiện tại, hầu hết các ý kiến đều giải thích rằng tôi có thể làm cho bản đồ chưa có thứ tự nhanh hơn bằng cách bố trí trước đủ không gian cho nó. Trong ứng dụng của chúng tôi, điều này là không thể: chúng tôi đang phát triển một hệ thống quản lý cơ sở dữ liệu và cần một bản đồ băm để lưu trữ một số dữ liệu trong một giao dịch (ví dụ: khóa thông tin). Vì vậy, bản đồ này có thể là tất cả mọi thứ từ 1 (người dùng chỉ thực hiện một lần chèn và cam kết) đến hàng tỷ mục nhập (nếu xảy ra quét toàn bộ bảng). Nó chỉ là không thể phân bổ trước đủ không gian ở đây (và chỉ cần phân bổ nhiều ngay từ đầu sẽ tiêu tốn quá nhiều bộ nhớ).
Hơn nữa, tôi xin lỗi, rằng tôi đã không trình bày câu hỏi của mình đủ rõ ràng: Tôi không thực sự quan tâm đến việc tạo nhanh bản đồ không có thứ tự (sử dụng bản đồ băm dày đặc của googles hoạt động tốt cho chúng tôi), tôi chỉ thực sự không hiểu sự khác biệt về hiệu suất lớn này đến từ đâu . Nó không thể chỉ là định vị trước (ngay cả khi có đủ bộ nhớ được định vị trước, bản đồ dày đặc có thứ tự độ lớn nhanh hơn so với bản đồ không có thứ tự, bản đồ đồng thời được hỗ trợ bằng tay của chúng ta bắt đầu bằng một mảng có kích thước 64 - vì vậy một bản đồ nhỏ hơn bản đồ không có thứ tự).
Vì vậy, lý do cho hiệu suất tồi tệ này là std::unordered_map
gì? Hoặc được hỏi theo cách khác: Người ta có thể viết một triển khai của std::unordered_map
giao diện tuân theo tiêu chuẩn và (gần) nhanh như bản đồ băm dày đặc của googles không? Hay có điều gì đó trong tiêu chuẩn buộc người thực hiện phải chọn một cách không hiệu quả để thực hiện nó?
CHỈNH SỬA 2:
Bằng cách lập hồ sơ, tôi thấy rằng rất nhiều thời gian được sử dụng cho các divion số nguyên. std::unordered_map
sử dụng số nguyên tố cho kích thước mảng, trong khi các triển khai khác sử dụng lũy thừa của hai. Tại sao std::unordered_map
sử dụng số nguyên tố? Để hoạt động tốt hơn nếu băm xấu? Đối với hàm băm tốt, imho không tạo ra sự khác biệt.
CHỈNH SỬA 3:
Đây là những con số cho std::map
:
inserts: 16462
get : 16978
Sooooooo: tại sao chèn vào một std::map
nhanh hơn chèn vào một std::unordered_map
... Ý tôi là WAT? std::map
có vị trí kém hơn (cây so với mảng), cần phân bổ nhiều hơn (mỗi lần chèn so với mỗi lần lặp lại + cộng ~ 1 cho mỗi lần va chạm) và quan trọng nhất: có độ phức tạp thuật toán khác (O (logn) so với O (1))!
SIZE
.