c++ - ঘুম না পেয়ে বেশ কয়েকটি পুনরাবৃত্তির পরে কেন এই বিলম্ব-লুপটি দ্রুত চলতে শুরু করে?




linux performance (2)

বিবেচনা:

#include <time.h>
#include <unistd.h>
#include <iostream>
using namespace std;

const int times = 1000;
const int N = 100000;

void run() {
  for (int j = 0; j < N; j++) {
  }
}

int main() {
  clock_t main_start = clock();
  for (int i = 0; i < times; i++) {
    clock_t start = clock();
    run();
    cout << "cost: " << (clock() - start) / 1000.0 << " ms." << endl;
    //usleep(1000);
  }
  cout << "total cost: " << (clock() - main_start) / 1000.0 << " ms." << endl;
}

এখানে উদাহরণ কোড দেওয়া আছে। টাইমিং লুপের প্রথম 26 টি পুনরাবৃত্তিতে, run ফাংশনটির জন্য প্রায় 0.4 এমএস খরচ হয় তবে তার পরে ব্যয়টি 0.2 এমএসে কমে যায়।

যখন usleep থাকে, তখন বিলম্ব-লুপটি সমস্ত রানের জন্য 0.4 এমএস নেয়, কখনই গতি usleep না। কেন?

কোডটি g++ -O0 (কোনও অপ্টিমাইজেশন নেই) দিয়ে সংকলিত হয়েছে, সুতরাং বিলম্বের লুপটি অপ্টিমাইজ করা যায় না। এটি ইন্টেল (আর) কোর (টিএম) i3-3220 সিপিইউ @ 3.30 গিগাহার্টজ উপর চালিত হয়েছে, 3.13.0-32-জেনেরিক Ubuntu 14.04.1 এলটিএস (বিশ্বস্ত Ubuntu 14.04.1 ) সহ।


আমাদের usleep জন্য কল করা প্রসঙ্গে স্যুইচ করতে পারে বা নাও পারে। এটি যদি করে, এটি না করলে তার চেয়ে বেশি সময় লাগবে।


২ ite টি পুনরাবৃত্তির পরে, লিনাক্স সিপিইউকে সর্বোচ্চ ঘড়ির গতি পর্যন্ত ছড়িয়ে দেয় যেহেতু আপনার প্রক্রিয়াটি তার পুরো সময়ের টানা কয়েকবার টুকরো টুকরো ব্যবহার করে।

যদি আপনি প্রাচীর-ঘড়ির সময়ের পরিবর্তে পারফরম্যান্স কাউন্টারগুলির সাথে চেক করেন, আপনি দেখতে পাচ্ছেন যে বিলম্ব-লুপের ভিত্তিতে মূল ক্লক চক্রগুলি স্থির ছিল, এটি নিশ্চিত করে যে এটি কেবল DVFS একটি প্রভাব (যা সমস্ত আধুনিক সিপিইউগুলি আরও বেশি শক্তি চালাতে ব্যবহার করে- বেশিরভাগ সময় দক্ষ ফ্রিকোয়েন্সি এবং ভোল্টেজ)।

আপনি যদি নতুন পাওয়ার-ম্যানেজমেন্ট মোডের (যেখানে হার্ডওয়্যারটি ঘড়ির গতিতে পুরো নিয়ন্ত্রণ নিয়ে থাকে) জন্য কার্নেল সমর্থনের সাহায্যে Skylake পরীক্ষা করে Skylake , Skylake আরও দ্রুত ঘটবে।

আপনি যদি এটি টার্বোর সাথে একটি ইন্টেল সিপিইউতে কিছু সময়ের জন্য চালিয়ে যান, আপনি সম্ভবত তাপীয় সীমাটির ঘড়ির গতিবেগ সর্বাধিক টেকসই স্থায়ী ফ্রিকোয়েন্সি হ্রাস করার জন্য একবার পুনরাবৃত্তি প্রতি সময় বাড়িয়ে দেখতে পাবেন see

একটি usleep পরিচয় করিয়ে দেওয়া লিনাক্সের সিপিইউ ফ্রিকোয়েন্সি গভর্নরকে ঘড়ির গতি usleep বাধা দেয়, কারণ প্রক্রিয়াটি ন্যূনতম ফ্রিকোয়েন্সিতেও 100% লোড জেনারেট করে না। (অর্থাৎ কার্নেলের তাত্পর্যপূর্ণ সিদ্ধান্ত নেয় যে সিপিইউ যে কাজ চলছে তার জন্য যথেষ্ট দ্রুত চলছে is)

অন্যান্য তত্ত্ব সম্পর্কে মন্তব্য :

পুনরায়: ডেভিডের তত্ত্ব যে usleep থেকে সম্ভাব্য প্রসঙ্গের পরিবর্তনটি ক্যাশে দূষিত করতে পারে : এটি সাধারণভাবে খারাপ ধারণা নয়, তবে এটি এই কোডটি ব্যাখ্যা করতে সহায়তা করে না।

এই পরীক্ষার জন্য ক্যাশে / টিএলবি দূষণ মোটেও গুরুত্বপূর্ণ নয় । স্ট্যাকের শেষ ব্যতীত স্মৃতি স্পর্শকারী টাইমিং উইন্ডোর মূলত কিছুই নেই। বেশিরভাগ সময় একটি ক্ষুদ্র লুপ (নির্দেশের ক্যাশে 1 লাইন) এ ব্যয় হয় যা কেবল স্ট্যাক মেমরির একটি ছোঁয়া। আমাদের usleep সময় যে কোনও সম্ভাব্য ক্যাশে দূষণ usleep এই কোডের জন্য সময়ের একটি ক্ষুদ্র ভগ্নাংশ (আসল কোডটি আলাদা হবে)!

এক্স 86 এর জন্য আরও বিশদে:

clock() কাছে কল clock() নিজেই ক্যাশে-মিস করতে পারে তবে কোনও কোড-ফেচ ক্যাশে মিস যা মাপা হয়েছে তার অংশ হওয়ার পরিবর্তে প্রারম্ভিক-সময় পরিমাপে বিলম্ব করে। clock() দ্বিতীয় কলটি clock() প্রায় কখনওই বিলম্বিত হবে না, কারণ এটি ক্যাশে এখনও গরম হওয়া উচিত।

run ফাংশনটি main থেকে আলাদা ক্যাশে লাইনে থাকতে পারে (যেহেতু জিসিসি মূলটিকে "ঠান্ডা" হিসাবে চিহ্নিত করে, তাই এটি কম অপ্টিমাইজড হয়ে যায় এবং অন্যান্য ঠান্ডা ফাংশন / ডেটা রাখে)। আমরা এক বা দুটি নির্দেশনা-ক্যাশে মিস করতে পারি আশা করতে পারি। তারা সম্ভবত একই 4 কে পৃষ্ঠায় রয়েছেন, যদিও main তাই প্রোগ্রামটির সময়সীমার অঞ্চলে প্রবেশের আগে সম্ভাব্য টিএলবি মিসকে ট্রিগার করে।

gcc -O0 ওপির কোডটি এরকম কিছুতে (গডবোল্ট কমপিলার এক্সপ্লোরার) সংকলন করবে: স্ট্যাকের সাথে লুপের পাল্টা রাখবে।

খালি লুপটি স্টুপের মেমোরিতে লুপকে পাল্টে রাখে, সুতরাং একটি সাধারণ ইনটেল x86 সিপিইউতে অপটির আইভিব্রিজ সিপিইউতে ~ 6 চক্রের এক পুনরাবৃত্তিতে লুপটি সঞ্চালিত হয়, স্টোর-ফরোয়ার্ডিং ল্যাটেন্সিকে ধন্যবাদ যা স্মৃতি গন্তব্য add করার অংশ ( পাঠযোগ্য সংশোধন-লিখন)। 100k iterations * 6 cycles/iteration 600k চক্র, যা বেশিরভাগ দম্পতি ক্যাশে মিসের অবদানকে প্রাধান্য দেয় (কোড-ফ্যাচ মিস করার জন্য প্রতি 200 ডলার চক্রগুলি যা সমাধান না হওয়া অবধি আরও নির্দেশাবলী জারি করতে বাধা দেয়)।

অফ-অর্ডার এক্সিকিউশন এবং স্টোর-ফরওয়ার্ডিংয়ের বেশিরভাগ ক্ষেত্রে স্ট্যাক অ্যাক্সেস করার সম্ভাব্য ক্যাশে মিসটি লুকানো উচিত ( call নির্দেশের অংশ হিসাবে)।

লুপ-কাউন্টারটি যদি একটি রেজিস্টারে রাখা হয় তবে 100 কে চক্র অনেক।





benchmarking