c - কেন আগায়ের নির্দেশের টেবিলের চেয়ে আলাদা, হাসওয়েতে মালস কেবল 3 টি চক্র নেয়?



assembly optimization (1)

আপনার লুপটি আবার দেখুন: movss xmm1, src এর পুরানো মানের উপর নির্ভরতা নেই, কারণ এর গন্তব্য কেবল লেখার জন্য । প্রতিটি পুনরাবৃত্তির mulss স্বাধীন। আদেশ- mulss কার্যকর করা এবং নির্দেশাবলীর স্তরের সমান্তরালতা কাজে লাগাতে পারে এবং যাতে আপনি অবশ্যই mulss বাধা দেন না।

Readingচ্ছিক পঠন: কম্পিউটার আর্কিটেকচারের শর্তে: রেজিস্টার নামকরণ একই আর্কিটেকচারাল রেজিস্টারের পুনরায় ব্যবহারের ওয়ার অ্যান্টি-নির্ভরতা ডেটা বিপত্তি এড়িয়ে চলে। (কিছু পাইপলাইনিং + নির্ভরতা-ট্র্যাকিং স্কিমগুলি রেজিস্ট্রার নামকরণের আগে সমস্ত সমস্যার সমাধান করেনি, তাই কম্পিউটার আর্কিটেকচারের ক্ষেত্রটি বিভিন্ন ধরণের ডেটা বিপদ থেকে বেরিয়ে একটি বড় চুক্তি করে।

টমাসুলোর অ্যালগরিদমের সাথে নাম পরিবর্তন করে প্রকৃত সত্য নির্ভরতা (লেখার পরে পড়ুন) ব্যতীত সমস্ত কিছু দূরে সরে যায়, সুতরাং যে কোনও নির্দেশিকা যেখানে গন্তব্যটি উত্সের নিবন্ধও নয়, সেই নিবন্ধের পুরাতন মান জড়িত নির্ভরতা শৃঙ্খলার সাথে কোনও ইন্টারঅ্যাকশন নেই। (মিথ্যা নির্ভরতা বাদে ইন্টেল সিপিইউতে popcnt মতো, এবং popcnt ছাড়াই একটি রেজিস্টারের অংশ লেখার মতো ( sqrtss xmm2, xmm1 mov al, 5 বা sqrtss xmm2, xmm1 ) সম্পর্কিত): কেন বেশিরভাগ x64 নির্দেশাবলী 32 এর উপরের অংশটি শূন্য করে? বিট রেজিস্টার )।

আপনার কোডে ফিরে যান:

.L13:
    movss   xmm1, DWORD PTR [rdi+rax*4]  
    mulss   xmm1, DWORD PTR [rsi+rax*4]   
    add     rax, 1                       
    cmp     cx, ax
    addss   xmm0, xmm1
    jg      .L13

লুপ বহনকারী নির্ভরতা (এক পুনরাবৃত্তি থেকে পরের দিকে) প্রতিটি:

  • xmm0 , addss xmm0, xmm1 দ্বারা পঠিত এবং লিখিত, যা হাসওয়েলে 3 টি চক্র addss xmm0, xmm1 রয়েছে।
  • rax , add rax, 1 দ্বারা পঠিত এবং লিখিত add rax, 1 । 1 সি লেটেন্সি, সুতরাং এটি সমালোচনামূলক-পথ নয়।

দেখে মনে হচ্ছে আপনি addss সময় / চক্র-গণনা সঠিকভাবে মাপলেন, কারণ 3 সি addss লেটেন্সিতে লুপের বাধা।

এটি প্রত্যাশিত: কোনও বিন্দুতে সিরিয়াল নির্ভরতা হ'ল একক পরিমাণে যোগ করা (ওরফে হ্রাস), ভেক্টর উপাদানগুলির মধ্যে গুণগুলি নয়।

বিভিন্ন লঘু অদক্ষতা সত্ত্বেও এ লুপটির জন্য এটি এখন পর্যন্ত প্রভাবশালী বাধা:

short i সিলি cmp cx, ax , যা একটি অতিরিক্ত অপারেন্ড-আকারের উপসর্গ গ্রহণ করে। ভাগ্যক্রমে, জিসিসি প্রকৃতপক্ষে add ax, 1 করা এড়াতে সক্ষম হয়েছিল কারণ সাইন ইন-ওভারফ্লো সি তে অপরিজ্ঞাত আচরণ is সুতরাং অপ্টিমাইজারটি ধরে নিতে পারে যে এটি ঘটেছিল না । (আপডেট: পূর্ণসংখ্যা প্রচারের নিয়মগুলি short জন্য এটি আলাদা করে তোলে , যাতে ইউবি এতে আসে না, তবে জিসিসি এখনও আইনতভাবে অনুকূল করতে পারে ret

আপনি যদি -mtune=intel , বা আরও ভাল, -march=haswell -mtune=intel দিয়ে সংকলন করতেন তবে -mtune=intel -march=haswell এবং jg একে অপরের পাশে jg যেখানে তারা ম্যাক্রো-ফিউজ করতে পারে।

আমি নিশ্চিত না যে আপনি নিজের টেবিলটিতে cmp একটি * রেখে কীভাবে নির্দেশাবলী যুক্ত করেন। (আপডেট: আমি নির্ভুলভাবে অনুমান করছিলাম যে আপনি IACA মতো একটি স্বরলিপি ব্যবহার করছেন, তবে সম্ভবত আপনি ছিলেন না)। তাদের উভয়ই ফিউজ না। একমাত্র ফিউশন ঘটছে mulss xmm1, [rsi+rax*4] 1 এর মাইক্রো-ফিউশন mulss xmm1, [rsi+rax*4]

এবং যেহেতু এটি একটি 2-অপারেন্ডের ALU নির্দেশিকা একটি পঠন-সংশোধন-লেখার গন্তব্য রেজিস্টার সহ, এটি হ্যাসওয়েলে আরওবিতে ম্যাক্রো-ফিউজড থেকে যায়। (ইস্যু সময়ে স্যান্ডিব্রিজে এটি আন-ল্যামিনেট করা হবে would) নোট করুন যে vmulss xmm1, xmm1, [rsi+rax*4] আন-ল্যামিনেট করবে

এগুলির কিছুই সত্যিই বিবেচ্য নয়, যেহেতু আপনি কেবলমাত্র ইউপি-থ্রুপুট সীমা ছাড়িয়ে এফপি-অ্যাড লেটেন্সিতে সম্পূর্ণরূপে বাধা। -ffast-math ছাড়া, সংকলক কিছুই করতে পারে না। -ffast-math , ঝনঝন সাধারণত একাধিক সঞ্চয়ের সাথে -ffast-math হবে এবং এটি স্বয়ংক্রিয়ভাবে ভেক্টরাইজ হবে যাতে তারা ভেক্টর -ffast-math হবে। সুতরাং আপনি যদি এল 1 ডি ক্যাশে আঘাত করেন তবে আপনি সম্ভবত ঘড়ি প্রতি 1 টি ভেক্টর বা স্কেলার এফপি যুক্ত হ্যাসওয়েলের থ্রুপুট সীমাটি পরিপূর্ণ করতে পারেন।

এফএমএ হ্যাসওয়েলে 5c ল্যাটেন্সি এবং 0.5c থ্রুটপুট হওয়ার কারণে আপনার 10 টি এফএমএ ফ্লাইটে রাখতে এবং 100 পিএইচ / পি 1 এফএমএ দ্বারা স্যাচুরেট করে সর্বোচ্চ এফএমএ থ্রুটপুট রাখার জন্য 10 টি সংগ্রহকারী প্রয়োজন হবে। (স্কাইলেক এফএমএর বিলম্বকে 4 টি চক্রের মধ্যে হ্রাস করেছে, এবং এফএমএ ইউনিটগুলিতে বহুগুণ, যোগ এবং এফএমএ চালায় So সুতরাং বাস্তবে এটি হ্যাসওয়েলের তুলনায় বেশি অ্যাড লেটেন্সি পেয়েছে))

(আপনি vaddps , কারণ আপনার প্রতিটি এফএমএর জন্য দুটি ভার দরকার other অন্য ক্ষেত্রে, আপনি 1.0 এর গুণক সহ একটি vaddps নির্দেশকে প্রতিস্থাপনের মাধ্যমে আসলে যুক্ত থ্রুপুট অর্জন করতে পারেন This যার অর্থ লুকানো আরও vaddps , তাই এটি আরও জটিল অ্যালগরিদমে সেরা যেখানে আপনার একটি অ্যাড রয়েছে যা প্রথম দিকে সমালোচনামূলক পথে নয়))

উত্তর: প্রতি বন্দরে উফস :

বন্দর ৫ এ লুপ প্রতি ১.১৯ উওপ রয়েছে, এটি ০.৫ প্রত্যাশার চেয়ে অনেক বেশি, উওপ প্রেরণকারী প্রতিটি বন্দরে একইভাবে উফ তৈরির চেষ্টা করছে এমন কি ব্যাপার?

হ্যা অনেকটা এরকমই.

উফগুলি এলোমেলোভাবে বরাদ্দ করা হয় না বা কোনওভাবে তারা চালিত হতে পারে এমন প্রতিটি বন্দর জুড়ে সমানভাবে বিতরণ করা হয়। আপনি ধরে নিয়েছেন যে add এবং সিএমপি উপগুলি সমানভাবে p0156 জুড়ে বিতরণ করবে, তবে এটি তেমন নয়।

ইস্যু পর্যায়ের পোর্টগুলির জন্য উওসকে ইতিমধ্যে কতগুলি ইউওপ ইতিমধ্যে সেই বন্দরের জন্য অপেক্ষা করছে তার ভিত্তিতে বন্দরে বরাদ্দ দেয়। যেহেতু addss কেবল addss 1 এ addss পারে (এবং এটি লুপের বাধা) তাই সাধারণত প্রচুর পি 1 উফ জারি হয় তবে কার্যকর হয় না। সুতরাং অন্য কয়েকটি উওপ পোর্ট 1 এ নির্ধারিত হবে। ( mulss অন্তর্ভুক্ত রয়েছে: বেশিরভাগ mulss ইউপগুলি 0 বন্দরটিতে নির্ধারিত হবে 0.)

তোলা-শাখাগুলি কেবল 6 বন্দরটিতে চলতে পারে পোর্ট 5 এর এই লুপটিতে কোনও উওপ নেই যা কেবল সেখানে চালাতে পারে, তাই এটি বহু-বন্দর উফগুলিকে আকর্ষণ করে শেষ হয়।

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

ইতিমধ্যে নির্ধারিত উফগুলির সময় নির্ধারণটি সাধারণত প্রাচীনতম-প্রস্তুত, কারণ আমি এটি বুঝতে পারি। এই সাধারণ অ্যালগরিদমটি খুব আশ্চর্যরকমই নয়, যেহেতু এটি আপনার সিপিইউ গলানো ছাড়াই প্রতিটি ঘড়ির চক্রের 60-এন্ট্রি আরএস থেকে প্রতিটি বন্দরের জন্য তৈরি ইনপুটগুলি সহ একটি পশম বেছে নিতে হবে। আইএলপি খুঁজে বের করে এবং তাদের অপব্যবহার করে এমন আউট-অফ-অর্ডার মেশিনারি হ'ল একটি আধুনিক সিপিইউতে কার্যকর শক্তি ব্যয়গুলির মধ্যে একটি, প্রকৃত কাজ করে এমন এক্সিকিউশন ইউনিটগুলির সাথে তুলনীয়।

সম্পর্কিত / আরও বিশদ: x86 উফ ঠিক কিভাবে নির্ধারিত হয়?

আরও কর্মক্ষমতা বিশ্লেষণ স্টাফ:

ক্যাশে মিস / শাখার ভুলগুলি বাদ দেওয়া, সিপিইউ-আবদ্ধ লুপগুলির জন্য প্রধান তিনটি প্রধান বাধা হ'ল:

  • নির্ভরতা চেইন (এই ক্ষেত্রে যেমন)
  • ফ্রন্ট-এন্ড থ্রুপুট (হাসওয়েলে প্রতি ঘড়িতে সর্বোচ্চ 4 টি ফিউজড-ডোমেন উপস)
  • এক্সিকিউশন পোর্টের বাধা বিপত্তিগুলি যেমন আপনার অনিবন্ধিত লুপের মতো প্রচুর উফগুলির জন্য p0 / p1, বা p2 / p3 প্রয়োজন হয়। নির্দিষ্ট বন্দরগুলির জন্য অব্যক্ত-ডোমেন উফগুলি গণনা করুন। সাধারণত আপনি সর্বোত্তম-কেস বিতরণ ধরে নিতে পারেন, উওপগুলি সহ অন্যান্য বন্দরগুলিতে চলাচলকারী ব্যস্ত বন্দরগুলি প্রায়শই চুরি করে না, তবে এটি কিছু ঘটে happen

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

কয়েকটি সংক্ষিপ্ত ক্রমিকের তুলনা করার জন্য তিনটিই করার উদাহরণস্বরূপ, আমার উত্তরটি দেখুন কোন অবস্থানে বা নীচে সেট বিট গণনা করার কার্যকর উপায় কী?

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

x86 ট্যাগ উইকিতে প্রচুর পারফরম্যান্স এবং রেফারেন্স লিঙ্কগুলিও দেখুন।

আপনার এফএমএ লুপ টিউন করা:

হ্যাঁ, হাসওলে ডট-প্রোডাক্ট এলএমডি থ্রুপুট এফএমএ ইউনিটগুলির অর্ধেক থ্রুটপুটটিতেই বাধা সৃষ্টি করবে, যেহেতু এটি প্রতি গুণিত + অ্যাডের জন্য দুটি বোঝা নেয়।

আপনি যদি B[i] = x * A[i] + y; বা sum(A[i]^2) , আপনি এফএমএ থ্রুপুটটি পূরণ করতে পারেন।

দেখে মনে হচ্ছে আপনি এখনও vmovaps লোডের গন্তব্যের মতো লিখিত-লিখিত ক্ষেত্রে এমনকি নিবন্ধের পুনরায় ব্যবহার এড়াতে চাইছেন, সুতরাং আপনি 8 দ্বারা রেজিস্ট্রেশন না করে রেজিস্টারগুলি থেকে দৌড়ে গেলেন । এটি ঠিক আছে, তবে অন্যান্য ক্ষেত্রে এটি কার্যকর হতে পারে।

এছাড়াও, ymm8-15 ব্যবহার করে কোড-আকারটি কিছুটা বাড়িয়ে ymm8-15 পারে যদি এর অর্থ হয় 2-বাইটের পরিবর্তে 3-বাইট VEX উপসর্গের প্রয়োজন হয়। মজাদার ঘটনা: vpxor ymm7,ymm7,ymm8 8 এর 3-বাইট vpxor ymm7,ymm7,ymm8 প্রয়োজন যখন vpxor ymm8,ymm8,ymm7 কেবলমাত্র একটি 2 বাইট ভেক্স উপসর্গ প্রয়োজন। চলাচলকারী ওপ্সের জন্য, বাছাই উত্সটি উচ্চ থেকে নিম্নে রেজি থাকে।

আমাদের বোঝার বাধাটি সর্বোত্তম কেস এফএমএ থ্রুটপুটটি অর্ধেক সর্বাধিক, সুতরাং তাদের বিলম্বতা আড়াল করতে আমাদের কমপক্ষে 5 ভেক্টর আহরণকারী প্রয়োজন। 8 টি ভাল, তাই নির্ভরশীল শৃঙ্খলে প্রচুর আলগা হয় যাতে তাদেরকে পি 0 / পি 1 এর জন্য অপ্রত্যাশিত বিলম্ব বা প্রতিযোগিতা থেকে যে কোনও বিলম্বের পরে ধরা দেয়। 7 বা সম্ভবত 6 টিও ভাল হবে: আপনার আনরোল ফ্যাক্টর 2 এর শক্তি হতে হবে না।

ঠিক 5 দ্বারা নিবন্ধভুক্ত করার অর্থ হ'ল নির্ভরতা শৃঙ্খলার জন্য আপনিও ঠিকঠাক স্থানে রয়েছেন । যে কোনও সময় কোনও এফএমএ সঠিক চক্রটিতে চালিত হয় না তার ইনপুট প্রস্তুত হওয়ার অর্থ depend নির্ভরতা শৃঙ্খলে হারিয়ে যাওয়া চক্র। কোনও লোড ধীর গতির হলে এটি ঘটতে পারে (উদাঃ এটি এল 1 ক্যাশে মিস করে এবং L2 এর জন্য অপেক্ষা করতে হবে), বা যদি অন্য কোনও নির্ভরশীলতা শৃঙ্খলা থেকে কোনও FMA এই এফএমএ নির্ধারিত ছিল বন্দরটি চুরি করে। (মনে রাখবেন যে শিডিউলিং ইস্যুর সময়ে ঘটে থাকে, তাই শিডিয়ুলারে বসে থাকা উফগুলি হয় হয় হয়00 FMA বা পোর্ট 1 এফএমএ, কোনও এফএমএ নয় যে কোনও বন্দর নিষ্ক্রিয় করতে পারে)।

যদি আপনি নির্ভরশীলতার শৃঙ্খলে কিছুটা ckিল ছেড়ে দেন, তবে আদেশ ছাড়াই কার্যকর করা এফএমএগুলিতে "ধরা" দিতে পারে, কারণ তারা কেবলমাত্র লোড ফলাফলের জন্য অপেক্ষা করে, থ্রুপুট বা বিলম্বিত করতে বাধা দেওয়া হবে না। @ ফরোয়ার্ডে (প্রশ্নের আপডেটে) পাওয়া গেছে যে 5 টি তালিকাভুক্তি এ লুপটির জন্য এল 1 ডি থ্রুপুট এর 93% থেকে 89.5% এ কর্মক্ষমতা হ্রাস পেয়েছে।

আমার ধারণা হ'ল 6 দ্বারা আনলোল করা (বিলম্বিতা আড়াল করার জন্য সর্বনিম্নের চেয়ে আরও একটি) এখানে ঠিক থাকবে, এবং 8-র মধ্যে আনرول হিসাবে সমান পারফরম্যান্সটি সম্পর্কে জানতে পারা আমরা যদি এফএমএ থ্রুপুটটি সর্বাধিক কাছাকাছি রাখি (কেবল লোডে বাধা না দিয়ে) থ্রুটপুট), সর্বনিম্নের চেয়ে আরও একটিও যথেষ্ট নাও হতে পারে।

আপডেট: @ ফরোয়ার্ডের পরীক্ষামূলক পরীক্ষাটি দেখায় যে আমার অনুমানটি ভুল ছিল । আনরোল 5 এবং আনরোল 6 এর মধ্যে বড় পার্থক্য নেই। এছাড়াও, আনরোল 15 টি ঘড়ি প্রতি 2x 256b লোডের তাত্ত্বিক সর্বাধিক থ্রুপুট থেকে আনোলোল 8 এর দ্বিগুণ কাছাকাছি। লুপে স্বতন্ত্র লোডের সাথে, বা স্বতন্ত্র লোড এবং কেবলমাত্র রেজিস্টার-কেবল এফএমএ দিয়ে পরিমাপ করা আমাদের এফএমএ নির্ভরতা শৃঙ্খলার সাথে মিথস্ক্রিয়াজনিত কারণে কতটা তা বলে দেয়। এমনকি সর্বোত্তম কেস 100% থ্রুপুট পাবেন না, যদি কেবলমাত্র পরিমাপের ত্রুটি এবং টাইমার বিঘ্ন কারণে ব্যাহত হয়। (লিনাক্স perf শুধুমাত্র ব্যবহারকারী-স্পেস চক্র পরিমাপ করে যদি না আপনি এটিকে রুট হিসাবে চালনা করেন তবে সময় এখনও বিঘ্নিত হ্যান্ডলারগুলিতে ব্যয় করা সময় অন্তর্ভুক্ত করে। এজন্য আপনার সিপিইউ ফ্রিকোয়েন্সিটি যখন নন-রুট হিসাবে চালানো হয় তবে 3.900GHz হিসাবে চালানো যেতে পারে cycles পরিবর্তে মূল এবং মাপার cycles হিসাবে cycles:u ।)

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

সহজ উপায় হ'ল লুপের অভ্যন্তরে দুটি পয়েন্টার-ইনক্রিমেন্ট করা। জটিল উপায় হ'ল অপরটির সাথে তুলনামূলকভাবে একটি অ্যারের সূচকের একটি ঝরঝরে কৌশল:

;; input pointers for x[] and y[] in rdi and rsi
;; size_t n  in rdx

    ;;; zero ymm1..8, or load+vmulps into them

    add             rdx, rsi             ; end_y
    ; lea rdx, [rdx+rsi-252]  to break out of the unrolled loop before going off the end, with odd n

    sub             rdi, rsi             ; index x[] relative to y[], saving one pointer increment

.unroll8:
    vmovaps         ymm0, [rdi+rsi]            ; *px, actually py[xy_offset]
    vfmadd231ps     ymm1, ymm0, [rsi]          ; *py

    vmovaps         ymm0,       [rdi+rsi+32]   ; write-only reuse of ymm0
    vfmadd231ps     ymm2, ymm0, [rsi+32]

    vmovaps         ymm0,       [rdi+rsi+64]
    vfmadd231ps     ymm3, ymm0, [rsi+64]

    vmovaps         ymm0,       [rdi+rsi+96]
    vfmadd231ps     ymm4, ymm0, [rsi+96]

    add             rsi, 256       ; pointer-increment here
                                   ; so the following instructions can still use disp8 in their addressing modes: [-128 .. +127] instead of disp32
                                   ; smaller code-size helps in the big picture, but not for a micro-benchmark

    vmovaps         ymm0,       [rdi+rsi+128-256]  ; be pedantic in the source about compensating for the pointer-increment
    vfmadd231ps     ymm5, ymm0, [rsi+128-256]
    vmovaps         ymm0,       [rdi+rsi+160-256]
    vfmadd231ps     ymm6, ymm0, [rsi+160-256]
    vmovaps         ymm0,       [rdi+rsi-64]       ; or not
    vfmadd231ps     ymm7, ymm0, [rsi-64]
    vmovaps         ymm0,       [rdi+rsi-32]
    vfmadd231ps     ymm8, ymm0, [rsi-32]

    cmp             rsi, rdx
    jb              .unroll8                 ; } while(py < endy);

vfmaddps -এর জন্য মেমরি অপারেন্ড হিসাবে একটি অ- vfmaddps অ্যাড্রেসিং মোড ব্যবহার করা vfmaddps ইস্যুতে আন-লেমিনেটেড না করে আউট-অফ-অর্ডার vfmaddps মাইক্রো-ফিউজড থাকতে দেয়। মাইক্রো ফিউশন এবং ঠিকানা মোড

সুতরাং আমার লুপটি 8 ভেক্টরগুলির জন্য 18 টি ফিউজড-ডোমেন উফ। ইন্ডেক্সযুক্ত অ্যাড্রেসিং মোডগুলি আন-ল্যামিনেশনের কারণে আপনার প্রতিটি ভিভোব্যাপ + ভিএফএমডিপিএস জুটির জন্য 3 টি পরিবর্তে 3 টি ফিউজড-ডোমেন উপ নেয়। তাদের উভয়েরই অবশ্যই প্রতি জোড়া 2 টি অব্যবহৃত-ডোমেন লোড উপ (পোর্ট 2/3) রয়েছে, সুতরাং এটি এখনও বাধা।

কম ফিউজড-ডোমেন উফস-অর্ডার এক্সিকিউশনটি আরও পুনরাবৃত্তিগুলি সামনে দেখায়, সম্ভাব্যভাবে এটি ক্যাশে মিস করতে আরও ভালভাবে শোষণে সহায়তা করে। যদিও আমরা কোনও এক্সিকিউশন ইউনিট (এই ক্ষেত্রে লোড উফস) এর সাথে বাধা দিই না কেন এমনকি কোনও ক্যাশে মিস না করে। হাইপারথ্রেডিংয়ের সাহায্যে, অন্য থ্রেডটি স্থবির না হলে আপনি কেবলমাত্র ফ্রন্ট-এন্ড ইস্যু ব্যান্ডউইথের প্রতিটি অন্যান্য চক্র পাবেন। যদি এটি লোড এবং পি 0/1 এর জন্য খুব বেশি প্রতিযোগিতা না করে তবে কম ফিউজড-ডোমেন উওপ একটি কোর ভাগ করার সময় এই লুপটি দ্রুত চালাতে দেবে। (উদাঃ সম্ভবত অন্যান্য হাইপার-থ্রেডে প্রচুর পোর্ট 5 / পোর্ট 6 এবং স্টোর উপস চলছে?)

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

তারপরে লুপের পরে: দক্ষ ক্লিনআপ হ'ল ছোট অ্যারেগুলির জন্য দক্ষ ভেক্টরাইজেশনের শক্ত অংশ যা আনرول ফ্যাক্টর বা বিশেষত ভেক্টরের প্রস্থের একাধিক নাও হতে পারে

    ...
    jb

    ;; If `n` might not be a multiple of 4x 8 floats, put cleanup code here
    ;; to do the last few ymm or xmm vectors, then scalar or an unaligned last vector + mask.

    ; reduce down to a single vector, with a tree of dependencies
    vaddps          ymm1, ymm2, ymm1
    vaddps          ymm3, ymm4, ymm3
    vaddps          ymm5, ymm6, ymm5
    vaddps          ymm7, ymm8, ymm7

    vaddps          ymm0, ymm3, ymm1
    vaddps          ymm1, ymm7, ymm5

    vaddps          ymm0, ymm1, ymm0

    ; horizontal within that vector, low_half += high_half until we're down to 1
    vextractf128    xmm1, ymm0, 1
    vaddps          xmm0, xmm0, xmm1
    vmovhlps        xmm1, xmm0, xmm0        
    vaddps          xmm0, xmm0, xmm1
    vmovshdup       xmm1, xmm0
    vaddss          xmm0, xmm1
    ; this is faster than 2x vhaddps

    vzeroupper    ; important if returning to non-AVX-aware code after using ymm regs.
    ret           ; with the scalar result in xmm0

শেষে অনুভূমিক যোগফল সম্পর্কে আরও তথ্যের জন্য , x86-তে অনুভূমিক ফ্লোট ভেক্টর যোগফলের দ্রুততম উপায়টি দেখুন। আমি যে দুটি 128 বি শ্যাফল ব্যবহার করেছি সেগুলির জন্য তাত্ক্ষণিক কন্ট্রোল বাইটেরও প্রয়োজন নেই, সুতরাং এটি কোড আকারের 2 বাইট এবং আরও সুস্পষ্ট shufps । (এবং কোড-সাইজের বনাম vpermilps 4 বাইট, কারণ সেই vpermilps সর্বদা 3-বাইট ভেক্স প্রিফিক্সের পাশাপাশি তাত্ক্ষণিক প্রয়োজন হয়)। অ্যাভেক্স 3-অপরেন্ড স্টাফ এসএসইয়ের সাথে তুলনা করে খুব সুন্দর, বিশেষত movhlps যখন অন্তর্নির্ভরগুলি সহ লেখার সময় আপনি movhlps সহজেই কোনও শীতল movhlps চয়ন করতে পারেন না।

আমি নির্দেশ অপ্টিমাইজেশনে একজন নবাগত।

আমি একটি সাধারণ ফাংশন ডটপে একটি সাধারণ বিশ্লেষণ করেছি যা দুটি ফ্লোট অ্যারের ডট পণ্য পেতে ব্যবহৃত হয়।

সি কোডটি নিম্নরূপ:

float dotp(               
    const float  x[],   
    const float  y[],     
    const short  n      
)
{
    short i;
    float suma;
    suma = 0.0f;

    for(i=0; i<n; i++) 
    {    
        suma += x[i] * y[i];
    } 
    return suma;
}

আমি ওয়েব testp অগ্নার ফগ দ্বারা সরবরাহিত পরীক্ষার ফ্রেমটি ব্যবহার করি।

এই ক্ষেত্রে ব্যবহৃত অ্যারেগুলি সারিবদ্ধ করা হয়:

int n = 2048;
float* z2 = (float*)_mm_malloc(sizeof(float)*n, 64);
char *mem = (char*)_mm_malloc(1<<18,4096);
char *a = mem;
char *b = a+n*sizeof(float);
char *c = b+n*sizeof(float);

float *x = (float*)a;
float *y = (float*)b;
float *z = (float*)c;

তারপরে আমি ফাংশনটি ডটপ বলি, n = 2048, পুনরাবৃত্তি = 100000:

 for (i = 0; i < repeat; i++)
 {
     sum = dotp(x,y,n);
 }

আমি এটি gcc 4.8.3 দিয়ে কম্পাইল অপশন -O3 দিয়ে সংকলন করি।

আমি এই অ্যাপ্লিকেশনটি এমন কোনও কম্পিউটারে সংকলন করি যা এফএমএ নির্দেশাবলী সমর্থন করে না, তাই আপনি দেখতে পাচ্ছেন যে কেবল এসএসই নির্দেশাবলী রয়েছে।

সমাবেশ কোড:

.L13:
        movss   xmm1, DWORD PTR [rdi+rax*4]  
        mulss   xmm1, DWORD PTR [rsi+rax*4]   
        add     rax, 1                       
        cmp     cx, ax
        addss   xmm0, xmm1
        jg      .L13

আমি কিছু বিশ্লেষণ করি:

          μops-fused  la    0    1    2    3    4    5    6    7    
movss       1          3             0.5  0.5
mulss       1          5   0.5  0.5  0.5  0.5
add         1          1   0.25 0.25               0.25   0.25 
cmp         1          1   0.25 0.25               0.25   0.25
addss       1          3         1              
jg          1          1                                   1                                                   -----------------------------------------------------------------------------
total       6          5    1    2     1     1      0.5   1.5

দৌড়ানোর পরে, আমরা ফলাফলটি পেয়েছি:

   Clock  |  Core cyc |  Instruct |   BrTaken | uop p0   | uop p1      
--------------------------------------------------------------------
542177906 |609942404  |1230100389 |205000027  |261069369 |205511063 
--------------------------------------------------------------------  
   2.64   |  2.97     | 6.00      |     1     | 1.27     |  1.00   

   uop p2   |    uop p3   |  uop p4 |    uop p5  |  uop p6    |  uop p7       
-----------------------------------------------------------------------   
 205185258  |  205188997  | 100833  |  245370353 |  313581694 |  844  
-----------------------------------------------------------------------          
    1.00    |   1.00      | 0.00    |   1.19     |  1.52      |  0.00           

দ্বিতীয় লাইনটি হ'ল ইনটেল রেজিস্টারগুলি থেকে পড়া মূল্য; তৃতীয় লাইনটি "ব্রটেকেন" শাখা নম্বর দ্বারা বিভক্ত।

সুতরাং আমরা দেখতে পারি, লুপটিতে বিশ্লেষণের সাথে একমত হয়ে 6 টি নির্দেশাবলী, 7 টি উওপ রয়েছে।

পোর্ট 0 পোর্ট 1 পোর্ট 5 পোর্ট 6 এ চালিত উওপের সংখ্যা বিশ্লেষণের কথার সাথে সমান। আমি মনে করি সম্ভবত উফ শিডিউলার এটি করে, এটি বন্দরগুলিতে ভার ভারসাম্য বজায় রাখার চেষ্টা করতে পারে, আমি ঠিক আছি?

আমি ল্যাপ প্রতি প্রায় 3 টি চক্র কেন জানি তা পুরোপুরি বুঝতে পারি না। অগ্নারের নির্দেশ সারণী অনুসারে, নির্দেশের mulss 5 এবং লুপগুলির মধ্যে নির্ভরতা রয়েছে, তাই যতদূর আমি দেখতে পাচ্ছি এটি প্রতি লুপে কমপক্ষে 5 টি চক্র গ্রহণ করা উচিত।

কেউ কি কিছু অন্তর্দৃষ্টি দিতে পারে?

================================================== ================

আমি নাসমে এই ফাংশনটির একটি অনুকূলিত সংস্করণ লেখার চেষ্টা করেছি, 8 এর ফ্যাক্টর দ্বারা লুপটি vfmadd231ps করে এবং vfmadd231ps নির্দেশ ব্যবহার করে:

.L2:
    vmovaps         ymm1, [rdi+rax]             
    vfmadd231ps     ymm0, ymm1, [rsi+rax]       

    vmovaps         ymm2, [rdi+rax+32]          
    vfmadd231ps     ymm3, ymm2, [rsi+rax+32]    

    vmovaps         ymm4, [rdi+rax+64]          
    vfmadd231ps     ymm5, ymm4, [rsi+rax+64]    

    vmovaps         ymm6, [rdi+rax+96]          
    vfmadd231ps     ymm7, ymm6, [rsi+rax+96]   

    vmovaps         ymm8, [rdi+rax+128]         
    vfmadd231ps     ymm9, ymm8, [rsi+rax+128]  

    vmovaps         ymm10, [rdi+rax+160]               
    vfmadd231ps     ymm11, ymm10, [rsi+rax+160] 

    vmovaps         ymm12, [rdi+rax+192]                
    vfmadd231ps     ymm13, ymm12, [rsi+rax+192] 

    vmovaps         ymm14, [rdi+rax+224]                
    vfmadd231ps     ymm15, ymm14, [rsi+rax+224] 
    add             rax, 256                    
    jne             .L2

ফলাফল:

  Clock   | Core cyc |  Instruct  |  BrTaken  |  uop p0   |   uop p1  
------------------------------------------------------------------------
 24371315 |  27477805|   59400061 |   3200001 |  14679543 |  11011601  
------------------------------------------------------------------------
    7.62  |     8.59 |  18.56     |     1     | 4.59      |     3.44


   uop p2  | uop p3  |  uop p4  |   uop p5  |   uop p6   |  uop p7  
-------------------------------------------------------------------------
 25960380  |26000252 |  47      |  537      |   3301043  |  10          
------------------------------------------------------------------------------
    8.11   |8.13     |  0.00    |   0.00    |   1.03     |  0.00        

সুতরাং আমরা দেখতে পাচ্ছি যে এল 1 ডেটা ক্যাশে 2 * 256 বিট / 8.59 এ পৌঁছায়, এটি চূড়ান্ত 2 * 256/8 এর খুব কাছে, ব্যবহার প্রায় 93%, এফএমএ ইউনিটটি কেবল 8 / 8.59 ব্যবহৃত হয়, শীর্ষটি 2 ​​* 8 হয় / 8, ব্যবহার 47%।

সুতরাং আমি মনে করি যে পিটার কর্ডসের প্রত্যাশা মতো আমি এল 1 ডি বাধা পৌঁছেছি।

================================================== ================

বোয়ানকে বিশেষ ধন্যবাদ, আমার প্রশ্নে বহু ব্যাকরণগত ত্রুটিগুলি ঠিক করুন।

================================================== ===============

পিটারের উত্তর থেকে, আমি পেয়েছি যে কেবল "পঠিত এবং লিখিত" নিবন্ধন নির্ভরতা হবে, "লেখক-কেবল" নিবন্ধগুলি নির্ভরতা হবে না।

সুতরাং আমি লুপে ব্যবহৃত নিবন্ধগুলি হ্রাস করার চেষ্টা করি এবং আমি 5 দ্বারা আনারোলিং করার চেষ্টা করি, যদি সবকিছু ঠিক থাকে তবে আমার একই বাধা, এল 1 ডি পূরণ করা উচিত।

.L2:
    vmovaps         ymm0, [rdi+rax]    
    vfmadd231ps     ymm1, ymm0, [rsi+rax]    

    vmovaps         ymm0, [rdi+rax+32]    
    vfmadd231ps     ymm2, ymm0, [rsi+rax+32]   

    vmovaps         ymm0, [rdi+rax+64]    
    vfmadd231ps     ymm3, ymm0, [rsi+rax+64]   

    vmovaps         ymm0, [rdi+rax+96]    
    vfmadd231ps     ymm4, ymm0, [rsi+rax+96]   

    vmovaps         ymm0, [rdi+rax+128]    
    vfmadd231ps     ymm5, ymm0, [rsi+rax+128]   

    add             rax, 160                    ;n = n+32
    jne             .L2 

ফলাফল:

    Clock  | Core cyc  | Instruct  |  BrTaken |    uop p0  |   uop p1  
------------------------------------------------------------------------  
  25332590 |  28547345 |  63700051 |  5100001 |   14951738 |  10549694   
------------------------------------------------------------------------
    4.97   |  5.60     | 12.49     |    1     |     2.93   |    2.07    

    uop p2  |uop p3   | uop p4 | uop p5 |uop p6   |  uop p7 
------------------------------------------------------------------------------  
  25900132  |25900132 |   50   |  683   | 5400909 |     9  
-------------------------------------------------------------------------------     
    5.08    |5.08     |  0.00  |  0.00  |1.06     |     0.00    

আমরা দেখতে পাচ্ছি 5 / 5.60 = 89.45%, এটি 8 এর মাধ্যমে ইউরোলিংয়ের চেয়ে কিছুটা ছোট, কিছু ভুল আছে কি?

================================================== ===============

ফলাফলটি দেখতে আমি 6, 7 এবং 15 এর মধ্যে লুপটি আনারল করার চেষ্টা করি। ফলাফলটি দ্বিগুণ করার জন্য আমি আবারও 5 এবং 8 দ্বারা আনরোল করেছিলাম।

ফলাফলটি অনুসরণ হিসাবে রয়েছে, আমরা এবার দেখতে পাচ্ছি ফলাফল আগের চেয়ে অনেক ভাল।

ফলাফল স্থিতিশীল না হলেও আনرولিং ফ্যাক্টরটি আরও বড় এবং ফলাফল আরও ভাল।

            | L1D bandwidth     |  CodeMiss | L1D Miss | L2 Miss 
----------------------------------------------------------------------------
  unroll5   | 91.86% ~ 91.94%   |   3~33    | 272~888  | 17~223
--------------------------------------------------------------------------
  unroll6   | 92.93% ~ 93.00%   |   4~30    | 481~1432 | 26~213
--------------------------------------------------------------------------
  unroll7   | 92.29% ~ 92.65%   |   5~28    | 336~1736 | 14~257
--------------------------------------------------------------------------
  unroll8   | 95.10% ~ 97.68%   |   4~23    | 363~780  | 42~132
--------------------------------------------------------------------------
  unroll15  | 97.95% ~ 98.16%   |   5~28    | 651~1295 | 29~68

================================================== ===================

আমি " https://gcc.godbolt.org " ওয়েবে জিসিসি 7.1 দিয়ে ফাংশনটি সংকলনের চেষ্টা করি

সংকলন বিকল্পটি হ'ল "-O3-মার্চ = হ্যাসওয়েল -ম্টিউন = ইন্টেল", এটি জিসিসি 4.8.3 এর মতো similar

.L3:
        vmovss  xmm1, DWORD PTR [rdi+rax]
        vfmadd231ss     xmm0, xmm1, DWORD PTR [rsi+rax]
        add     rax, 4
        cmp     rdx, rax
        jne     .L3
        ret




micro-optimization