আমরা সি ++ এ একটি উচ্চ কার্যকারিতা সমালোচনামূলক সফ্টওয়্যার তৈরি করছি। সেখানে আমাদের একসাথে হ্যাশ মানচিত্র প্রয়োজন এবং এটি প্রয়োগ করা হয়েছে। সুতরাং আমরা এটি নির্ধারণের জন্য একটি বেঞ্চমার্ক লিখেছিলাম, আমাদের সমবর্তী হ্যাশ মানচিত্রের তুলনায় কত ধীর std::unordered_map
।
তবে, std::unordered_map
অবিশ্বাস্যরূপে ধীর বলে মনে হচ্ছে ... সুতরাং এটি আমাদের মাইক্রো-বেঞ্চমার্ক (সমবর্তী মানচিত্রের জন্য আমরা লকটি অপ্টিমাইজড না হয়ে যায় তা নিশ্চিত করার জন্য একটি নতুন থ্রেড তৈরি করেছি এবং মনে রাখবেন যে আমি কখনই 0 টি প্রবেশ করিনি কারণ আমি এর সাথে বেঞ্চমার্কও রেখেছি google::dense_hash_map
, যার একটি নাল মান প্রয়োজন):
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;
(সম্পাদনা: পুরো উত্স কোডটি এখানে পাওয়া যাবে: http://pastebin.com/vPqf7eya )
এর জন্য ফলাফল std::unordered_map
:
inserts: 35126
get : 2959
এর জন্য google::dense_map
:
inserts: 3653
get : 816
আমাদের হ্যান্ড ব্যাকড সমবর্তী মানচিত্রের জন্য (যা লক করে, যদিও বেনমার্কটি একক থ্রেডযুক্ত - তবে একটি পৃথক স্প্যান থ্রেডে):
inserts: 5213
get : 2594
আমি যদি পাইথ্রেড সমর্থন ছাড়াই বেঞ্চমার্ক প্রোগ্রামটি সংকলন করি এবং মূল থ্রেডে সমস্ত কিছু চালিত করি তবে আমি আমাদের হাতের ব্যাকড সমবর্তী মানচিত্রের জন্য নিম্নলিখিত ফলাফলগুলি পেয়েছি:
inserts: 4441
get : 1180
আমি নিম্নলিখিত কমান্ডটি দিয়ে সংকলন করছি:
g++-4.7 -O3 -DNDEBUG -I/tmp/benchmap/sparsehash-2.0.2/src/ -std=c++11 -pthread main.cc
সুতরাং বিশেষত সন্নিবেশগুলি std::unordered_map
অত্যন্ত ব্যয়বহুল বলে মনে হয় - 35 সেকেন্ড বনাম অন্যান্য মানচিত্রের জন্য 3-5 সেকেন্ড। এছাড়াও দেখার সময়টি বেশ বেশি বলে মনে হচ্ছে।
আমার প্রশ্ন: এটা কেন? স্ট্যাকওভারফ্লোতে আমি আর একটি প্রশ্ন পড়েছি যেখানে কেউ জিজ্ঞাসা করে, কেন std::tr1::unordered_map
তার নিজের প্রয়োগের চেয়ে ধীর। সেখানে সর্বাধিক রেট দেওয়া উত্তর বলেছে যে std::tr1::unordered_map
আরও জটিল ইন্টারফেস প্রয়োগ করা দরকার। তবে আমি এই যুক্তিটি দেখতে পাচ্ছি না: আমরা আমাদের সমকালীন_ম্যাপে বালতি পদ্ধতির ব্যবহার করি, std::unordered_map
একটি বালতি-পদ্ধতিরও ব্যবহার করি ( google::dense_hash_map
না, তবে std::unordered_map
আমাদের হাতের ব্যাকড কনক্যুরেন্সী-নিরাপদ সংস্করণের চেয়ে কমপক্ষে দ্রুত হওয়া উচিত?)) তা ছাড়া আমি ইন্টারফেসে এমন কিছু দেখতে পাচ্ছি না যা এমন বৈশিষ্ট্যকে বাধ্য করে যা হ্যাশ মানচিত্রটিকে খারাপভাবে সম্পাদন করে ...
সুতরাং আমার প্রশ্ন: এটা std::unordered_map
কি খুব ধীর বলে মনে হচ্ছে সত্য ? যদি না: ভুল কী? যদি হ্যাঁ: এর কারণ কী।
এবং আমার মূল প্রশ্ন: কেন std::unordered_map
এত ভয়াবহ ব্যয়বহুল হিসাবে মান সন্নিবেশ করা হচ্ছে (শুরুতে আমরা পর্যাপ্ত জায়গা সংরক্ষণ করলেও এটি আরও ভাল পারফর্ম করে না - তাই পুনরায় ভাগ করা সমস্যা বলে মনে হচ্ছে না)?
সম্পাদনা:
প্রথমত: হ্যাঁ উপস্থাপিত বেঞ্চমার্কটি ত্রুটিবিহীন নয় - এটি কারণ এটির সাথে আমরা প্রচুর পরিমাণে খেলেছি এবং এটি কেবল একটি হ্যাক (উদাহরণস্বরূপ uint64
ints উত্পন্ন করার বিতরণটি বাস্তবে ভাল ধারণা হবে না, 0 লুপে বাদ দিন) এটি একধরণের বোকা ইত্যাদি ...)।
এই মুহুর্তে বেশিরভাগ মন্তব্য ব্যাখ্যা করে, আমি এর জন্য পর্যাপ্ত স্থানটি পূর্বনির্ধারণ করে আনর্ডার্ড_ম্যাপটি আরও দ্রুত করতে পারি। আমাদের প্রয়োগে এটি কেবল সম্ভব নয়: আমরা একটি ডেটাবেস পরিচালনা ব্যবস্থা তৈরি করছি এবং লেনদেনের সময় কিছু তথ্য সংরক্ষণের জন্য একটি হ্যাশ মানচিত্রের প্রয়োজন (উদাহরণস্বরূপ লকিং তথ্য)। সুতরাং এই মানচিত্রটি 1 (ব্যবহারকারী কেবল একটি সন্নিবেশ করায় এবং প্রতিশ্রুতি দেয়) থেকে কয়েক বিলিয়ন এন্ট্রি পর্যন্ত (যদি পুরো টেবিল স্ক্যানগুলি ঘটে থাকে) হতে পারে। এখানে পর্যাপ্ত জায়গাটি বানাতে অসম্ভব (এবং শুরুতে প্রচুর পরিমাণ বরাদ্দ দেওয়া খুব বেশি স্মৃতি গ্রহন করবে)।
তদুপরি, আমি ক্ষমাপ্রার্থী, আমি আমার প্রশ্নটি যথেষ্ট পরিষ্কারভাবে প্রকাশ করি নি: আনর্ডার্ড_ম্যাপটি দ্রুত তৈরি করতে আমি সত্যিই আগ্রহী নই (গুগলসের ঘন হ্যাশ মানচিত্রটি আমাদের পক্ষে ভাল কাজ করে), আমি সত্যিই বুঝতে পারি না যে এই বিশাল পারফরম্যান্সের পার্থক্যগুলি কোথা থেকে এসেছে? । এটি কেবল পূর্বনির্ধারণ হতে পারে না (এমনকি পর্যাপ্ত পূর্বরূপযুক্ত মেমরি থাকা সত্ত্বেও, ঘন মানচিত্রটি আনর্ডার্ড_ম্যাপের চেয়ে দ্রুততর আকারের ক্রম, আমাদের হাতের ব্যাকযুক্ত সমবর্তী মানচিত্র size৪ আকারের অ্যারে দিয়ে শুরু হয় - সুতরাং অর্ডারড_ম্যাপের চেয়ে ছোট একটি)।
তাহলে এই খারাপ পারফরম্যান্সের কারণ কী std::unordered_map
? বা অন্যভাবে জিজ্ঞাসা করা হয়েছে: কেউ std::unordered_map
ইন্টারফেসের একটি বাস্তবায়ন লিখতে পারেন যা স্ট্যান্ডার্ড কনফর্ম এবং প্রায় (প্রায়) গুগলসের ঘন হ্যাশ মানচিত্রের তত দ্রুত? বা স্ট্যান্ডার্ডে এমন কিছু আছে যা প্রয়োগকারীকে কার্যকর করার জন্য এটি একটি অদক্ষ উপায় চয়ন করে?
সম্পাদনা 2:
প্রোফাইলিংয়ের মাধ্যমে আমি দেখতে পাচ্ছি যে পূর্ণসংখ্যার বিভাজনের জন্য প্রচুর সময় ব্যবহৃত হয়। std::unordered_map
অ্যারের আকারের জন্য মৌলিক সংখ্যাগুলি ব্যবহার করে, অন্য বাস্তবায়নগুলি দুটির শক্তি ব্যবহার করে। std::unordered_map
মৌলিক সংখ্যা কেন ব্যবহার করে ? হ্যাশ খারাপ হলে আরও ভাল পারফর্ম করতে হবে? ভাল hashes জন্য এটি imho কোন পার্থক্য নেই।
সম্পাদনা 3:
এটির জন্য নম্বরগুলি std::map
:
inserts: 16462
get : 16978
Sooooooo: কেন একটি intoোকানো std::map
চেয়ে দ্রুত মধ্যে সন্নিবেশ করা হয় std::unordered_map
... মানে ওয়াট? std::map
আরও খারাপ লোকেশন (ট্রি বনাম অ্যারে) রয়েছে, আরও বরাদ্দ করা দরকার (প্রতি সংঘর্ষের জন্য প্রতি সন্নিবেশ বনাম + প্রতিটি সংঘর্ষের জন্য প্লাস ~ 1) এবং, সবচেয়ে গুরুত্বপূর্ণ: আরেকটি অ্যালগোরিদমিক জটিলতা রয়েছে (ও (লগন) বনাম ও (1))!
SIZE
।