আমি খুব সম্প্রতি আমার সংস্থায় বিভিন্ন ডেটা স্ট্রাকচারের একটি মানদণ্ড চালিয়েছি যাতে আমার মনে হয় আমার একটি শব্দ ফেলে দেওয়া দরকার। কোনও কিছুকে সঠিকভাবে বেঞ্চমার্ক করা খুব জটিল।
স্থির করা মাপকাঠি
ওয়েবে আমরা খুব কমই খুঁজে পাই (যদি কখনও হয়) একটি ভাল ইঞ্জিনিয়ারড বেঞ্চমার্ক। আজ অবধি আমি কেবলমাত্র সাংবাদিক হিসাবে সম্পন্ন বেঞ্চমার্কগুলি পেয়েছি (বেশ দ্রুত এবং কার্পেটের নিচে কয়েক ডজন ভেরিয়েবল)।
1) আপনার ক্যাশে ওয়ার্মিং সম্পর্কে বিবেচনা করা উচিত
বেঞ্চমার্কে চলমান বেশিরভাগ লোকেরা টাইমার স্বতন্ত্রতার জন্য ভয় পান, তাই তারা হাজার হাজার বার তাদের স্টাফ চালায় এবং পুরো সময় নেয়, তারা প্রতিটি অপারেশনের জন্য একই হাজার বার গ্রহণে সতর্ক থাকে এবং তারপরে সেই তুলনামূলক বিবেচনা করে।
সত্য সত্য, বাস্তব বিশ্বে এটি কিছুটা অর্থপূর্ণ নয়, কারণ আপনার ক্যাশে উষ্ণ হবে না এবং আপনার অপারেশন সম্ভবত একবার ডাকা হবে। অতএব আপনাকে আরডিটিএসসি ব্যবহার করে বেঞ্চমার্ক করা দরকার, এবং কেবলমাত্র একবার তাদের কল করার সময়সীমা। ইন্টেল একটি কাগজ করেছেন বর্ণনা কিভাবে RDTSC ব্যবহার করবেন (প্রোগ্রামটি স্থির শুরুতে একটি cpuid নির্দেশ ব্যবহার পাইপলাইন ঘনিষ্ঠরূপে, এবং এটি কলিং অন্তত 3 বার)।
2) আরডিটিএসসি নির্ভুলতা পরিমাপ
আমি এটি করার পরামর্শও দিচ্ছি:
u64 g_correctionFactor; // number of clocks to offset after each measurement to remove the overhead of the measurer itself.
u64 g_accuracy;
static u64 const errormeasure = ~((u64)0);
#ifdef _MSC_VER
#pragma intrinsic(__rdtsc)
inline u64 GetRDTSC()
{
int a[4];
__cpuid(a, 0x80000000); // flush OOO instruction pipeline
return __rdtsc();
}
inline void WarmupRDTSC()
{
int a[4];
__cpuid(a, 0x80000000); // warmup cpuid.
__cpuid(a, 0x80000000);
__cpuid(a, 0x80000000);
// measure the measurer overhead with the measurer (crazy he..)
u64 minDiff = LLONG_MAX;
u64 maxDiff = 0; // this is going to help calculate our PRECISION ERROR MARGIN
for (int i = 0; i < 80; ++i)
{
u64 tick1 = GetRDTSC();
u64 tick2 = GetRDTSC();
minDiff = std::min(minDiff, tick2 - tick1); // make many takes, take the smallest that ever come.
maxDiff = std::max(maxDiff, tick2 - tick1);
}
g_correctionFactor = minDiff;
printf("Correction factor %llu clocks\n", g_correctionFactor);
g_accuracy = maxDiff - minDiff;
printf("Measurement Accuracy (in clocks) : %llu\n", g_accuracy);
}
#endif
এটি একটি তাত্ক্ষণিক পরিমাপক এবং সময়ে সময়ে একটি -10 ** 18 (b৪ বিট প্রথম নেগেটিভ মান) পেতে এড়াতে এটি সমস্ত পরিমাপ করা মানগুলির সর্বনিম্ন গ্রহণ করবে।
অন্তর্নিহিত সমাবেশ নয় অভ্যন্তরীণ ব্যবহার লক্ষ্য করুন। প্রথম ইনলাইন অ্যাসেমব্লি আজকাল কমই সংকলক দ্বারা সমর্থিত হয়, তবে সবচেয়ে বড় কথা, সংকলকটি ইনলাইন অ্যাসেমব্লির চারপাশে পুরো অর্ডারিং বাধা তৈরি করে কারণ এটি অভ্যন্তরের স্থির বিশ্লেষণ করতে পারে না, তাই এটি সত্যিকারের বিশ্বের জিনিসগুলির মানদণ্ডে সমস্যা, বিশেষত যখন স্টাফকে কেবল কল করার সময় একদা. সুতরাং একটি অন্তর্নিহিত এখানে উপযুক্ত, কারণ এটি সংকলক নির্দেশাবলীর ফ্রি-পুনঃ-ক্রমটি ভাঙ্গেনি।
3) পরামিতি
শেষ সমস্যাটি হ'ল লোকেরা সাধারণত দৃশ্যের খুব কম পরিবর্তনের জন্য পরীক্ষা করে। একটি ধারক কর্মক্ষমতা দ্বারা প্রভাবিত হয়:
- বরাদ্দকরণ
- রয়েছে টাইপ আকার
- অনুলিখিত প্রকারের অনুলিপি অপারেশন, অ্যাসাইনমেন্ট অপারেশন, মুভ অপারেশন, নির্মাণ অপারেশন বাস্তবায়নের ব্যয়।
- পাত্রে উপাদানগুলির সংখ্যা (সমস্যার আকার)
- প্রকারের তুচ্ছ 3.-অপারেশন রয়েছে
- টাইপ হ'ল পিওডি
পয়েন্ট 1 গুরুত্বপূর্ণ কারণ কনটেইনাররা সময়ে সময়ে বরাদ্দ দেয় এবং সিআরটি "নতুন" বা কিছু ব্যবহারকারীর সংজ্ঞায়িত অপারেশন যেমন পুল বরাদ্দ বা ফ্রিলিস্ট বা অন্যান্য ব্যবহার করে বরাদ্দ করে তবে তা অনেকটাই গুরুত্বপূর্ণ ...
( পিটি 1 সম্পর্কে আগ্রহী লোকদের জন্য, সিস্টেম বরাদ্দকারীর কার্যকারিতা প্রভাব সম্পর্কে গেমদেবের রহস্য থ্রেডে যোগ দিন )
পয়েন্ট 2 কারণ কিছু পাত্রে (বলুন এ) আশেপাশে স্টাফ অনুলিপি করতে সময় হারাবে, এবং প্রকারের ওপরের অংশটি তত বড়। সমস্যাটি হ'ল অন্য কনটেইনার বিয়ের সাথে তুলনা করার সময় এ ছোট ধরণের জন্য বি এর উপর জয়লাভ করতে পারে এবং বড় ধরণের জন্য হারাতে পারে।
পয়েন্ট 3 পয়েন্ট 2 এর সমান, এটি কোনও ওজন ফ্যাক্টরের দ্বারা ব্যয়কে বহুগুণ বাদ দেয়।
পয়েন্ট 4 হ'ল ক্যাশে ইস্যুতে মিশ্রিত বড় ও একটি প্রশ্ন। কিছু খারাপ-জটিলতার পাত্রে খুব কম সংখ্যক প্রকারের জন্য কম-জটিলতার পাতাগুলি ছাড়িয়ে যেতে পারে (যেমন map
বনাম vector
, কারণ তাদের ক্যাশে স্থানীয় অবস্থান ভাল, তবে map
স্মৃতিটিকে টুকরো টুকরো করে)। এবং তারপরে কোনও ক্রসিং পয়েন্টে, তারা হারাবে, কারণ অন্তর্ভুক্ত সামগ্রিক আকারটি মূল স্মৃতিতে "ফাঁস" হতে শুরু করে এবং ক্যাশে অনুপস্থিত কারণ, অ্যাসিম্পটোটিক জটিলতা অনুভূত হওয়া শুরু হতে পারে।
পঞ্চম পয়েন্টটি সংকলনকারীরা ফাঁকা বা সংকলনের সময় নগণ্য যেগুলি স্টাফকে এলিডে সক্ষম করতে সক্ষম। এটি কিছু ক্রিয়াকলাপকে ব্যাপকভাবে অনুকূল করতে পারে, কারণ পাত্রে টেম্পলেট হয়, সুতরাং প্রতিটি ধরণের নিজস্ব কর্মক্ষমতা প্রোফাইল থাকবে।
পয়েন্ট point এর মতো পয়েন্ট 6, পিওডিরা এই সত্যটি থেকে লাভবান হতে পারে যে অনুলিপি নির্মাণ কেবল একটি মেমকি, এবং কিছু ধারক এই ক্ষেত্রেগুলির জন্য একটি নির্দিষ্ট প্রয়োগ করতে পারে, আংশিক টেম্পলেট বিশেষায়নের ব্যবহার করে, বা টিফির বৈশিষ্ট্য অনুসারে অ্যালগরিদম নির্বাচন করতে SFINAE S
ফ্ল্যাট মানচিত্র সম্পর্কে
স্পষ্টতই ফ্ল্যাট মানচিত্রটি সাজানো ভেক্টরের মোড়ক যেমন লোকি অ্যাসোসভেেক্টর, তবে কিছু পরিপূরক আধুনিকীকরণ সি -+ 11 এর সাথে আসে, একক উপাদানগুলিকে sertোকাতে এবং মুছতে তাত্পর করতে সরানো শব্দার্থবিজ্ঞানের শোষণ করে।
এটি এখনও অর্ডারযুক্ত ধারক। বেশিরভাগ লোকের সাধারণত ক্রম অংশ প্রয়োজন হয় না, তাই অস্তিত্ব unordered..
।
আপনি বিবেচনা করেছেন যে সম্ভবত আপনার একটি প্রয়োজন flat_unorderedmap
? যা এমন কিছু google::sparse_map
বা এর মতো কিছু হবে। একটি খোলা ঠিকানা হ্যাশ ম্যাপ।
ওপেন অ্যাড্রেস হ্যাশ ম্যাপগুলির সমস্যাটি হ'ল সেই সময়ে rehash
তাদের চারপাশের সমস্ত কিছু নতুন প্রসারিত সমতল ভূমিতে অনুলিপি করতে হবে, যেখানে একটি মানহীন মানচিত্রে কেবল হ্যাশ সূচকটি পুনরায় তৈরি করতে হবে, যখন বরাদ্দকৃত ডেটা যেখানে থাকে সেখানেই থাকে। অবশ্যই অসুবিধে হ'ল স্মৃতিটি নরকের মতো খণ্ডিত হয়।
একটি খোলা ঠিকানা হ্যাশ ম্যাপে পুনঃস্থাপনের মানদণ্ডটি যখন ক্ষমতাটি লোড ফ্যাক্টরের দ্বারা গুণিত বালতি ভেক্টরের আকারকে ছাড়িয়ে যায়।
একটি সাধারণ লোড ফ্যাক্টর হ'ল 0.8
; অতএব, আপনার এটির যত্ন নেওয়া দরকার, যদি আপনি নিজের হ্যাশ মানচিত্রটি পূরণের আগে প্রাক আকার করতে পারেন তবে সর্বদা এটির প্রাক-আকার: intended_filling * (1/0.8) + epsilon
এটি আপনাকে পূরণের সময় সমস্ত কিছু তাত্পর্যপূর্ণভাবে পুনঃস্থাপন এবং পুনরায় কপি না করার গ্যারান্টি দেয়।
বদ্ধ ঠিকানা মানচিত্রের সুবিধা ( std::unordered..
) হ'ল আপনাকে সেই পরামিতিগুলি সম্পর্কে যত্ন নিতে হবে না।
তবে এটি boost::flat_map
একটি আদেশযুক্ত ভেক্টর; অতএব, এটিতে সর্বদা লগ (এন) অ্যাসিপটোটিক জটিলতা থাকবে যা খোলা ঠিকানার হ্যাশ মানচিত্রের (এমরোটাইজড ধ্রুবক সময়) এর চেয়ে কম ভাল। আপনার এটিও বিবেচনা করা উচিত।
বেঞ্চমার্ক ফলাফল
এটি বিভিন্ন মানচিত্রের ( int
কী এবং __int64
/ somestruct
মান হিসাবে) এবং এর সাথে জড়িত একটি পরীক্ষা std::vector
।
পরীক্ষিত প্রকারের তথ্য:
typeid=__int64 . sizeof=8 . ispod=yes
typeid=struct MediumTypePod . sizeof=184 . ispod=yes
সন্নিবেশ
সম্পাদনা করুন:
আমার পূর্ববর্তী ফলাফলগুলিতে একটি বাগ অন্তর্ভুক্ত ছিল: তারা প্রকৃত অর্ডার সন্নিবেশ পরীক্ষা করেছে, যা ফ্ল্যাট মানচিত্রের জন্য খুব দ্রুত আচরণ প্রদর্শন করে।
আমি এই ফলাফলগুলি পরে এই পৃষ্ঠাতে রেখেছি কারণ তারা আকর্ষণীয়।
এটি সঠিক পরীক্ষা:
আমি বাস্তবায়ন পরীক্ষা করে দেখেছি, এখানে ফ্ল্যাট মানচিত্রে একটি মুলতুবি সাজানোর মতো কোনও জিনিস নেই। প্রতিটি সন্নিবেশ ফ্লাইতে বাছাই করে, তাই এই মানদণ্ডটি অ্যাসিম্পটোটিক প্রবণতাগুলি প্রদর্শন করে:
মানচিত্র: ও (এন * লগ (এন))
হ্যাশম্যাপস: ও (এন)
ভেক্টর এবং ফ্ল্যাটম্যাপস: ও (এন * এন)
সতর্কবাণী : এর জন্য পরকালে 2 পরীক্ষার std::map
এবং উভয় flat_map
গুলি করছে বগী এবং আসলে পরীক্ষা আদেশ সন্নিবেশ (বনাম অন্যান্য পাত্রে জন্য র্যান্ডম সন্নিবেশ হ্যাঁ এটা দুঃখিত বিভ্রান্তিকর হচ্ছে।):
আমরা দেখতে পেলাম যে আদেশযুক্ত সন্নিবেশ, ফলাফল পিছনে চাপানো এবং অত্যন্ত দ্রুত extremely তবে, আমার বেঞ্চমার্কের অ-চার্টেড ফলাফল থেকে, আমি এটিও বলতে পারি যে এটি ব্যাক-সন্নিবেশের জন্য পরম অনুকূলতার কাছাকাছি নয়। 10 কে উপাদানগুলিতে, নিখুঁত ব্যাক-সন্নিবেশ অনুকূলতা একটি প্রাক-সংরক্ষিত ভেক্টরের উপর প্রাপ্ত হয়। যা আমাদের 3 মিলিয়ন চক্র দেয়; অর্ডার সন্নিবেশের জন্য আমরা এখানে 4.8M পর্যবেক্ষণ করি flat_map
(অতএব সর্বোত্তমের 160%)।
বিশ্লেষণ: মনে রাখবেন এটি ভেক্টরের জন্য এটি 'এলোমেলো সন্নিবেশ', সুতরাং বিশাল পরিমাণ 1 বিলিয়ন চক্র প্রতিটি সন্নিবেশে উপাত্তের উপরে (এক উপাদান দ্বারা একটি উপাদান) অর্ধেক স্থানান্তরিত হওয়া থেকে আসে (গড়তে)।
3 টি উপাদানের এলোমেলো অনুসন্ধান (1 টির মধ্যে ঘড়ির পুনঃনির্ধারণ)
আকারে = 100
আকারে = 10000
পুনরাবৃত্তির
100 এর বেশি আকারের (কেবলমাত্র মিডিয়ামপড টাইপ)
ওপরের আকার 10000 (কেবলমাত্র মিডিয়ামপড টাইপ)
লবণের চূড়ান্ত দানা
শেষ পর্যন্ত আমি "বেঞ্চমার্কিং P3 পিটি 1" (সিস্টেম বরাদ্দকারী) এ ফিরে আসতে চেয়েছিলাম। সাম্প্রতিক একটি পরীক্ষায় আমি যে ওপেন অ্যাড্রেস হ্যাশ ম্যাপটির বিকাশ করেছি তার কার্য সম্পাদন করছি, কিছু std::unordered_map
ব্যবহারের ক্ষেত্রে উইন্ডোজ 7 এবং উইন্ডোজ 8 এর মধ্যে পারফরম্যান্সের ব্যবধান 3000% এর চেয়ে বেশি পরিমাপ করেছি ( এখানে আলোচনা করা হয়েছে )।
যা আমাকে উপরের ফলাফলগুলি সম্পর্কে পাঠককে সতর্ক করতে চায় (সেগুলি উইন 7-এ তৈরি হয়েছিল): আপনার মাইলেজটি ভিন্ন হতে পারে।
শুভেচ্ছান্তে