যদি আপনি মনে করেন একটি -৪-বিট ডিআইভি নির্দেশকে দুটি দ্বারা বিভক্ত করার একটি ভাল উপায়, তবে এতে অবাক হওয়ার কিছু নেই যে সংকলকটির এসএমআউট আউটপুট আপনার হাতের লিখিত কোডটিকেও হারিয়ে ফেলবে, এমনকি -O0
(দ্রুত সংকলন করুন, কোনও অতিরিক্ত অপ্টিমাইজেশন নেই, এবং / পরে মেমরিতে স্টোর / পুনরায় লোড করুন) প্রতিটি সি স্টেটমেন্টের আগে যাতে কোনও ডিবাগার ভেরিয়েবলগুলি সংশোধন করতে পারে)।
দক্ষ asm কীভাবে লিখতে হয় তা শিখতে Agner Fog এর অপ্টিমাইজিং অ্যাসেমব্লিক গাইডটি দেখুন । নির্দিষ্ট সিপিইউগুলির জন্য সুনির্দিষ্ট বিবরণের জন্য তার নির্দেশাবলী টেবিল এবং একটি মাইক্রোয়ার্ক গাইডও রয়েছে। এছাড়াও দেখুনএক্স 86 আরও পারফেক্ট লিঙ্কের জন্য ট্যাগ উইকি।
হাতে লিখিত asm দিয়ে সংকলককে প্রহার করার বিষয়ে এই আরও সাধারণ প্রশ্নটি দেখুন: ইনলাইন সমাবেশের ভাষাটি কি সি সি ++ কোডের চেয়ে ধীর? । টিএল: ডিআর: হ্যাঁ যদি আপনি এটি ভুল করেন (এই প্রশ্নের মতো)।
সাধারণত আপনি সংকলকটিকে তার কাজটি করতে দিচ্ছেন, বিশেষত যদি আপনি সি ++ লেখার চেষ্টা করেন যা দক্ষতার সাথে সংকলন করতে পারে । এছাড়াও দেখুন সংকলিত ভাষার চেয়ে সমাবেশ কি দ্রুত? । এই ঝরঝরে স্লাইডগুলির উত্তরগুলির একটির লিঙ্কগুলি দেখায় যে কীভাবে বিভিন্ন সি সংকলক শীতল কৌশলগুলি সহ কিছু সাধারণ ফাংশন অনুকূল করে। ম্যাট গডবোল্টের সিপিপিসন ২০১7 আলাপ " আমার কম্পাইলার ইদানীং আমার জন্য কী করেছে? সংকলকের idাকনাটি আনবোল্ট করা একই ধরণের শিরায় ।
even:
mov rbx, 2
xor rdx, rdx
div rbx
ইনটেল div r64
হাসওলে, 32 -96 চক্রের বিলম্বের সাথে এবং 21-74 চক্র প্রতি একটির একটির মধ্য দিয়ে 36 টি ওপস । (আরবিএক্স এবং শূন্য আরডিএক্স সেটআপ করতে 2 টি উওস প্লাস করুন, তবে আউট-অফ-অর্ডার এক্সিকিউশনটি তাড়াতাড়ি চালাতে পারে)। ডিআইভির মতো উচ্চ-উওপ-কাউন্টের নির্দেশাবলী মাইক্রোকোডযুক্ত, এটি ফ্রন্ট-এন্ড বাধাও সৃষ্টি করতে পারে। এই ক্ষেত্রে, বিলম্বিতা সবচেয়ে প্রাসঙ্গিক কারণ কারণ এটি লুপ বহনকারী নির্ভরতা শৃঙ্খলার অংশ।
shr rax, 1
একই স্বাক্ষরবিহীন বিভাগটি করে: এটি 1 উওপ, 1 সি ল্যাটেন্সি সহ এবং প্রতি ক্লক চক্র 2 চালাতে পারে।
তুলনার জন্য, 32-বিট বিভাগ দ্রুত, তবে এখনও ভয়ঙ্কর বনাম শিফট। idiv r32
9 টি উওস, 22-29 সি ল্যাটেন্সি এবং হাসওয়েলে 8-10c প্রতি থ্রুপুট one
আপনি যেমনটি জিসিসির এসএম-O0
আউটপুট ( গডবোল্ট সংকলক এক্সপ্লোরার ) দেখে দেখে নিতে পারেন, এটি কেবল শিফট নির্দেশাবলী ব্যবহার করে । ঝনঝন -O0
কম্পাইল naively মত চিন্তা, এমনকি দুইবার 64-বিট IDIV ব্যবহার নেই। (অপ্টিমাইজ করার সময়, সংকলকগুলি আইডিআইভির উভয় আউটপুট ব্যবহার করে যখন উত্সটি একই অপারেশনগুলির সাথে বিভাগ এবং মডিউলাস করে, যদি তারা আইডিআইভি ব্যবহার করে তবে)
জিসিসির সম্পূর্ণরূপে নিষ্পাপ মোড নেই; এটি সর্বদা জিম্পল এর মাধ্যমে রূপান্তর করে যার অর্থ কিছু "অপ্টিমাইজেশন" অক্ষম করা যায় না । এর মধ্যে বিভাগ-বাই-ধ্রুবককে স্বীকৃতি দেওয়া এবং আইডিআইভি এড়ানোর জন্য শিফ্ট (2 পাওয়ার) বা একটি স্থির-পয়েন্ট গুণিত ইনভার্স (2-এর শক্তি নয়) অন্তর্ভুক্ত রয়েছে ( div_by_13
উপরের গডবোল্ট লিঙ্কে দেখুন)।
gcc -Os
(আকারের জন্য অনুকূলিতকরণ) অ-পাওয়ার-অফ -2 বিভাগের জন্য আইডিআইভি ব্যবহার করে , দুর্ভাগ্যক্রমে এমনকি এমন ক্ষেত্রেও যেখানে গুণক বিপরীত কোডটি কিছুটা বড় তবে খুব দ্রুত much
সংকলককে সহায়তা করছে
(এই ক্ষেত্রে সংক্ষিপ্তসার: ব্যবহার uint64_t n
)
প্রথমত, কেবলমাত্র অনুকূলিত সংকলক আউটপুট দেখতে আকর্ষণীয়। ( -O3
)। -O0
গতি মূলত অর্থহীন।
আপনার asm আউটপুটটি দেখুন (গডবোল্টে, বা জিসিসি / ঝনক সমাবেশ আউটপুট থেকে "শব্দ" কীভাবে সরিয়ে ফেলবেন তা দেখুন )। কম্পাইলার প্রথম স্থানে অনুকূল কোড দেখা যায় না কখন: একটি উপায় যে নির্দেশিকা অধিক কোড তৈরীর মধ্যে কম্পাইলার সাধারণত সেরা পন্থা আপনার সি / সি ++ উৎস লেখা । আপনাকে asm জানতে হবে, এবং কী দক্ষ তা জানতে হবে তবে আপনি এই জ্ঞানকে পরোক্ষভাবে প্রয়োগ করেন। সংকলকগুলিও ধারণাগুলির একটি ভাল উত্স: কখনও কখনও ঝনঝন কিছু ভাল কাজ করে, এবং আপনি একই কাজটি করার জন্য জিসিসি হ্যান্ড হোল্ড করতে পারেন: এই উত্তরটি দেখুন এবং নীচে @ ভিড্রাকের কোডটিতে অ-নিবন্ধভুক্ত লুপটি দিয়ে আমি কী করেছি))
এই পদ্ধতির বহনযোগ্য, এবং 20 বছরের মধ্যে কিছু ভবিষ্যতের সংকলক ভবিষ্যতের হার্ডওয়্যার (x86 বা না) এর ক্ষেত্রে দক্ষ যা কিছু সংকলন করতে পারে, সম্ভবত নতুন আইএসএ এক্সটেনশন বা অটো-ভেক্টরাইজিং ব্যবহার করে। 15 বছর আগে থেকে হাতে লেখা x86-64 asm সাধারণত স্কাইলেকের জন্য অনুকূলভাবে সুর করা যায় না। যেমন তুলনা করুন & ব্রাঞ্চ ম্যাক্রো-ফিউশন তখন আর বিদ্যমান ছিল না। একটি মাইক্রোআরকিটেকচারের জন্য হস্তনির্মিত এএসএমের জন্য এখন সর্বোত্তম কী অন্যান্য বর্তমান এবং ভবিষ্যতের সিপিইউগুলির জন্য অনুকূল নাও হতে পারে। @ জনফাউন্ডের উত্তরের মন্তব্যগুলি এএমডি বুলডোজার এবং ইন্টেল হাসওয়ের মধ্যে প্রধান পার্থক্য নিয়ে আলোচনা করেছে, যা এই কোডটিতে একটি বড় প্রভাব ফেলে। তাত্ত্বিকভাবে, g++ -O3 -march=bdver3
এবং g++ -O3 -march=skylake
সঠিক জিনিস করবে। (বা -march=native
।) অথবা -mtune=...
অন্য সিপিইউগুলি সমর্থন নাও করতে পারে এমন নির্দেশাবলী ব্যবহার না করে কেবল সুর করার জন্য।
আমার অনুভূতি হ'ল সংকলককে Asm করতে গাইড করা যা আপনার বর্তমান CPU এর পক্ষে ভাল যা ভবিষ্যতের সংকলকগুলির জন্য সমস্যা হওয়া উচিত নয়। কোডটি রূপান্তর করার উপায়গুলি খুঁজতে তারা বর্তমান সংকলকগুলির চেয়ে আশাবাদী এবং ভবিষ্যতের সিপিইউগুলির জন্য কাজ করে এমন কোনও উপায় খুঁজে পেতে পারে। নির্বিশেষে, ভবিষ্যতের x86 সম্ভবত বর্তমান x86 এর ভাল যে কোনও কিছুতে ভয়ঙ্কর হবে না এবং ভবিষ্যতের সংকলক আপনার সি উত্স থেকে ডেটা মুভমেন্টের মতো কিছু বাস্তবায়নের সময় কোনও asm-সুনির্দিষ্ট সমস্যাগুলি এড়াতে পারে, যদি এটি আরও ভাল কিছু না দেখায়।
হস্ত-লিখিত asm অপ্টিমাইজারের জন্য একটি কালো-বাক্স, সুতরাং যখন ইনলাইনিং কোনও ইনপুটকে একটি সংকলন-সময় ধ্রুবক করে তখন ধ্রুবক-প্রচার কাজ করে না। অন্যান্য অপ্টিমাইজেশানগুলিও প্রভাবিত হয়। Asm ব্যবহার করার আগে https://gcc.gnu.org/wiki/DontUseInlineAsm পড়ুন । (এবং এমএসভিসি-স্টাইলের ইনলাইন asm এড়ান: ইনপুট / আউটপুটগুলিকে মেমরির মধ্য দিয়ে যেতে হবে যা ওভারহেড যুক্ত করে ))
এই ক্ষেত্রে : আপনার n
স্বাক্ষরিত ধরণ রয়েছে এবং জিসিসিতে এসএআর / এসএইচআর / এডিডি ক্রম ব্যবহার করে যা সঠিক বৃত্তাকার দেয়। (আইডিআইভি এবং গাণিতিক-শিফট "রাউন্ড" নেতিবাচক ইনপুটগুলির জন্য আলাদাভাবে দেখুন, এসএআর ইনস সেট রেফ ম্যানুয়াল এন্ট্রি দেখুন )। (আইডিকে যদি জিসিসি চেষ্টা করেও তা প্রমাণ n
করতে ব্যর্থ হয় যে নেতিবাচক হতে পারে না বা কী। স্বাক্ষরিত ওভারফ্লো অনির্ধারিত আচরণ, তাই এটি সক্ষম হওয়া উচিত ছিল))
আপনার ব্যবহার করা উচিত ছিল uint64_t n
, সুতরাং এটি কেবল এসআরআর করতে পারে। এবং সুতরাং এটি এমন সিস্টেমে পোর্টেবল long
যা কেবলমাত্র 32-বিট (যেমন x86-64 উইন্ডোজ)।
BTW, জিসিসি এর অপ্টিমাইজ এ এস এম আউটপুট প্রশংসনীয় ভাল (ব্যবহার দেখায় unsigned long n
) : ভেতরের লুপ তা inlines main()
এই আছে:
# from gcc5.4 -O3 plus my comments
# edx= count=1
# rax= uint64_t n
.L9: # do{
lea rcx, [rax+1+rax*2] # rcx = 3*n + 1
mov rdi, rax
shr rdi # rdi = n>>1;
test al, 1 # set flags based on n%2 (aka n&1)
mov rax, rcx
cmove rax, rdi # n= (n%2) ? 3*n+1 : n/2;
add edx, 1 # ++count;
cmp rax, 1
jne .L9 #}while(n!=1)
cmp/branch to update max and maxi, and then do the next n
অভ্যন্তরীণ লুপটি শাখাবিহীন এবং লুপ বহনকারী নির্ভরতা শৃঙ্খলার সমালোচনাপূর্ণ পথ:
- 3-উপাদান এলইএ (3 চক্র)
- সেমিভভ (হাসওলে 2 টি চক্র, ব্রডওয়েলে 1c বা তার পরে)।
মোট: পুনরাবৃত্তি প্রতি 5 চক্র, বিলম্ব বাধা । আউট-অফ-অর্ডার এক্সিকিউশনটি এর সাথে সমান্তরালভাবে সমস্ত কিছুর যত্ন নেয় (তত্ত্ব অনুসারে: আমি সত্যিই এটি 5 সি / ইটারে চলে কিনা তা দেখার জন্য পারফ কাউন্টারগুলির সাথে পরীক্ষা করিনি)।
পতাকা ইনপুট cmov
(টেস্ট দ্বারা উত্পাদিত), দ্রুত RAX ইনপুট চেয়ে উত্পাদন করতে (LEA-> যে MOV থেকে), তাই এটি সমালোচনামূলক পথে নয়।
একইভাবে, সিএমওভের আরডিআই ইনপুট উত্পাদনকারী এমওভি-> এসআরআর সমালোচনামূলক পথে বন্ধ রয়েছে, কারণ এটি এলইএর চেয়েও দ্রুত। আইভিব্রিজে এমওভির পরে এবং পরে শূন্যের বিলম্ব হয় (রেজিস্টার-নাম পরিবর্তনের সময় পরিচালিত)। (এটি এখনও পাইপলাইনে একটি পদক্ষেপ নেবে এবং একটি স্লট লাগে, সুতরাং এটি নিখরচায় নয়, কেবল শূন্যের বিলম্ব)। এলইএ ডিপ চেইনে অতিরিক্ত এমওভি অন্যান্য সিপিইউগুলিতে বাধার এক অংশ।
সিএমপি / জেনও সমালোচনামূলক পথের অংশ নয়: এটি লুপ বহনকারী নয়, কারণ নিয়ন্ত্রণের নির্ভরতাগুলি সমালোচনামূলক পথে ডেটা নির্ভরতার বিপরীতে শাখার পূর্বাভাস + অনুমানমূলক সম্পাদন দ্বারা পরিচালিত হয়।
সংকলককে মারধর করছে
জিসিসি এখানে বেশ ভাল কাজ করেছে। এটি inc edx
পরিবর্তেadd edx, 1
ব্যবহার করে একটি কোড বাইট সংরক্ষণ করতে পারে কারণ আংশিক-পতাকা-সংশোধন নির্দেশাবলীর জন্য কেউ P4 এবং এর মিথ্যা-নির্ভরতা সম্পর্কে চিন্তা করে না।
এটি সমস্ত এমওভি নির্দেশাবলী এবং পরীক্ষাও সংরক্ষণ করতে পারে: এসআরআর সিএফ = সেট করে বিট সেট করে, তাই আমরা / এর cmovc
পরিবর্তে ব্যবহার করতে পারি ।test
cmovz
### Hand-optimized version of what gcc does
.L9: #do{
lea rcx, [rax+1+rax*2] # rcx = 3*n + 1
shr rax, 1 # n>>=1; CF = n&1 = n%2
cmovc rax, rcx # n= (n&1) ? 3*n+1 : n/2;
inc edx # ++count;
cmp rax, 1
jne .L9 #}while(n!=1)
আরেকটি চতুর কৌতূহলের জন্য @ জনফাউন্ডের উত্তর দেখুন: এসএইচআর এর পতাকা ফলাফলের উপর ব্রাঞ্চ করার পাশাপাশি সিএমওভির জন্য এটি ব্যবহার করে সিএমপি সরিয়ে ফেলুন: শুরু হলে এন 1 (বা 0) হলে শূন্য। (মজাদার ঘটনা: নেহালেম বা তার আগেরের গণনা সহ এসএইচআর! আপনি পতাকাটির ফলাফলগুলি পড়লে স্টল তৈরি করে। তারা এটিকে এটিকে এককভাবে উপস্থাপিত করেছে though শিফট বাই -1 বিশেষ এনকোডিং ঠিক আছে, যদিও)
এমওভি এড়ানো এলোমেলোভাবে হাসওলে মোটেও সহায়তা করে না ( x86 এর এমওভি আসলেই কি "মুক্ত" হতে পারে? কেন আমি এটিকে কেন পুনরুত্পাদন করতে পারি না? )। এটি ইনটেল প্রি-আইভিবি, এবং এএমডি বুলডোজার-পরিবারের মতো সিপিইউগুলিতে উল্লেখযোগ্যভাবে সহায়তা করে, যেখানে এমওভি শূন্য-বিলম্বিত নয়। সংকলকটির নষ্ট MOV নির্দেশাবলী সমালোচনামূলক পথে প্রভাবিত করে। বিডির জটিল-এলইএ এবং সিএমওভ উভয়ই নিম্নতর ল্যাটেন্সি (যথাক্রমে 2 সি এবং 1 সি), সুতরাং এটি বিলম্বের একটি বড় ভগ্নাংশ। এছাড়াও, থ্রুপুট বাধাগুলি একটি সমস্যা হয়ে দাঁড়ায়, কারণ এতে কেবল দুটি পূর্ণসংখ্যক ALU পাইপ রয়েছে। @ জনফাউন্ডের উত্তর দেখুন , যেখানে তার একটি এএমডি সিপিইউ থেকে সময় ফলাফল রয়েছে।
এমনকি হ্যাসওয়েলে, এই সংস্করণটি মাঝে মধ্যে কিছু সময় বিলম্ব এড়িয়ে কিছুটা সহায়তা করতে পারে যেখানে একটি অ-সমালোচক ইউওপ সমালোচনামূলক পথে একটি থেকে কার্যকরকরণের বন্দরটি চুরি করে 1 চক্রের মাধ্যমে কার্যকর করতে বিলম্ব করে। (একে রিসোর্স কোন্দল বলা হয়)। এটি একটি রেজিস্টারও সংরক্ষণ করে, যা n
আন্তঃবিবাহিত লুপের সমান্তরালে একাধিক মান করার ক্ষেত্রে সহায়তা করতে পারে (নীচে দেখুন)।
এলইএর প্রচ্ছন্নতা ইন্টেল এসএনবি-পরিবার সিপিইউগুলিতে ঠিকানা মোডের উপর নির্ভর করে । 3 সি 3 উপাদানগুলির জন্য ( [base+idx+const]
যা দুটি পৃথক সংযোজন করে), তবে 2 বা কম উপাদান (1 টি যোগ) সহ কেবল 1 সি। কিছু সিপিইউ (যেমন কোর 2) এমনকি একটি একক চক্রের 3-উপাদান এলইএ করে, তবে এসএনবি-পরিবার তা করে না। সবচেয়ে খারাপ, ইনটেল এসএনবি-পরিবার বিলম্বকে মানসম্পন্ন করে যাতে 2c উওস না থাকে , অন্যথায় 3-উপাদান এলইএ বুলডোজারের মতো কেবল 2 সি হবে। (3-উপাদান এলইএ এএমডি-তেও ধীরে ধীরে, কেবল তত বেশি নয়)।
সুতরাং lea rcx, [rax + rax*2]
/ inc rcx
শুধুমাত্র 2C লেটেন্সি, দ্রুত চেয়ে lea rcx, [rax + rax*2 + 1]
, Haswell মত ইন্টেল SnB পরিবার সিপিইউ উপর। ব্রেক-ইন্ বিডি-তে, এবং কোর 2-এ আরও খারাপ। এটির জন্য অতিরিক্ত ইউওপ ব্যয় হয়, যা সাধারণত 1 সি লেটেন্সি বাঁচাতে উপযুক্ত নয়, তবে লটেন্সি এখানে প্রধান প্রধান বাধা এবং অতিরিক্ত ইউওপ থ্রুপুট পরিচালনা করার জন্য হাসওলের একটি বিস্তৃত পর্যাপ্ত পাইপলাইন রয়েছে।
কোনও জিসিসি, আইসিসি, বা ঝনঝন নয় (গডবোল্টে) এসএইচআর এর সিএফ আউটপুট ব্যবহার করে, সর্বদা একটি অ্যান্ড বা টেস্ট ব্যবহার করে । নির্বোধ সংকলক। : পি এগুলি জটিল যন্ত্রের দুর্দান্ত টুকরো, তবে একজন চালাক মানুষ প্রায়শই ছোট আকারের সমস্যায় তাদের পরাজিত করতে পারে। (এটি সম্পর্কে চিন্তা করতে কয়েক হাজার থেকে আরও কয়েক লক্ষ বেশি সময় দেওয়া হয়েছে, অবশ্যই! সংকলকগণ কাজগুলি করার প্রতিটি সম্ভাব্য উপায় অনুসন্ধান করার জন্য বিস্তৃত অ্যালগরিদম ব্যবহার করে না, কারণ অনেকগুলি ইনিল্যান্ড কোডটি অনুকূলিত করার ক্ষেত্রে এটি খুব বেশি সময় নিতে পারে, যা কোনটি তারা সবচেয়ে ভাল করে। তারা লক্ষ্য মাইক্রোআরকিটেকচারে পাইপলাইনও মডেল করে না, অন্তত আইএসিএ বা অন্যান্য স্থিতিশীল-বিশ্লেষণ সরঞ্জামগুলির মতো একই বিশদে নয় ; তারা কেবল কিছু হিউরিস্টিক্স ব্যবহার করে))
সরল লুপ আন্রোলিং সাহায্য করবে না ; লুপ ওভারহেড / থ্রুপুট উপর নয়, একটি লুপ বহনশীল নির্ভরশীল শৃঙ্খলার বিরতিতে এই লুপের বাধা। এর অর্থ এটি হাইপারথ্রেডিং (বা অন্য কোনও ধরণের এসএমটি) দিয়ে ভাল করবে, যেহেতু সিপিইউতে দুটি থ্রেড থেকে নির্দেশনা ইন্টারলাইভ করার জন্য প্রচুর সময় রয়েছে। এর অর্থ লুপটি সমান্তরাল হওয়া main
, তবে এটি ঠিক আছে কারণ প্রতিটি থ্রেড কেবলমাত্র n
মানগুলির একটি ব্যাপ্তি পরীক্ষা করতে পারে এবং ফলস্বরূপ একজোড়া পূর্ণসংখ্যার উত্পাদন করতে পারে।
একক থ্রেডের মধ্যে হাতে হাতে ইন্টারলিভিং কার্যকরও হতে পারে । সমান্তরালভাবে এক জোড়া সংখ্যার জন্য ক্রমটি গণনা করুন, যেহেতু প্রত্যেকে কেবলমাত্র দু'জন রেজিস্টার নেন এবং তারা সকলেই একই max
/ আপডেট করতে পারবেন maxi
। এটি আরও নির্দেশ-স্তরের সমান্তরালতা তৈরি করে ।
কৌশলটি সিদ্ধান্ত নিচ্ছে যে আরম্ভের মানগুলির আরেকটি জোড় পাওয়ার আগে সমস্ত n
মান পৌঁছে যাওয়া পর্যন্ত অপেক্ষা করা উচিত কিনা , অথবা অন্য ক্রমের জন্য রেজিস্টারগুলিকে স্পর্শ না করে শেষ শর্তে পৌঁছানো মাত্র একটির জন্য একটি নতুন সূচনা পয়েন্ট পাওয়া যায় কিনা। সম্ভবত প্রতিটি চেইন দরকারী ডেটাতে কাজ করা সবচেয়ে ভাল, অন্যথায় আপনাকে শর্তসাপেক্ষে এর পাল্টা বাড়িয়ে তুলতে হবে।1
n
এমনকি আপনি এসএসই প্যাকড-তুলনা স্টাফ দিয়ে শর্তসাপেক্ষে ভেক্টর উপাদানগুলির জন্য কাউন্টারকে বাড়িয়ে তুলতে পারেন যেখানে এখনও n
পৌঁছেনি 1
। এবং তারপরে সিমডিয়াল শর্তসাপেক্ষে বৃদ্ধি বাস্তবায়নের আরও দীর্ঘতর লম্বাতাটি আড়াল করার জন্য আপনাকে আরও n
মূল্যবোধের ভেক্টরগুলিকে বাতাসে রাখার প্রয়োজন হবে । কেবলমাত্র 256b ভেক্টর (4x uint64_t
) দিয়ে মূল্যবান।
আমি মনে করি একটি 1
"স্টিকি" সনাক্তকরণের সর্বোত্তম কৌশলটি হ'ল আপনি কাউন্টারকে বাড়ানোর ক্ষেত্রে যুক্ত করা সমস্ত-এর ভেক্টরকে মাস্ক করা। সুতরাং 1
আপনি কোনও উপাদানটিতে একটি দেখার পরে , ইনক্রিমেন্ট-ভেক্টরের শূন্য থাকবে এবং + = 0 একটি অপ-বিকল্প।
ম্যানুয়াল ভেক্টরাইজেশনের জন্য অনির্ধারিত ধারণা
# starting with YMM0 = [ n_d, n_c, n_b, n_a ] (64-bit elements)
# ymm4 = _mm256_set1_epi64x(1): increment vector
# ymm5 = all-zeros: count vector
.inner_loop:
vpaddq ymm1, ymm0, xmm0
vpaddq ymm1, ymm1, xmm0
vpaddq ymm1, ymm1, set1_epi64(1) # ymm1= 3*n + 1. Maybe could do this more efficiently?
vprllq ymm3, ymm0, 63 # shift bit 1 to the sign bit
vpsrlq ymm0, ymm0, 1 # n /= 2
# FP blend between integer insns may cost extra bypass latency, but integer blends don't have 1 bit controlling a whole qword.
vpblendvpd ymm0, ymm0, ymm1, ymm3 # variable blend controlled by the sign bit of each 64-bit element. I might have the source operands backwards, I always have to look this up.
# ymm0 = updated n in each element.
vpcmpeqq ymm1, ymm0, set1_epi64(1)
vpandn ymm4, ymm1, ymm4 # zero out elements of ymm4 where the compare was true
vpaddq ymm5, ymm5, ymm4 # count++ in elements where n has never been == 1
vptest ymm4, ymm4
jnz .inner_loop
# Fall through when all the n values have reached 1 at some point, and our increment vector is all-zero
vextracti128 ymm0, ymm5, 1
vpmaxq .... crap this doesn't exist
# Actually just delay doing a horizontal max until the very very end. But you need some way to record max and maxi.
আপনার হাতে লিখিত asm এর পরিবর্তে অন্তর্নিহিতগুলি দিয়ে এটি প্রয়োগ করতে এবং করা উচিত।
অ্যালগরিদমিক / বাস্তবায়ন উন্নতি:
আরও দক্ষ asm সহ কেবল একই যুক্তিকে বাস্তবায়ন করা ছাড়াও যুক্তিটিকে সহজ করার উপায়গুলি অনুসন্ধান করুন বা অপ্রয়োজনীয় কাজ এড়ানো উচিত। যেমন ক্রমগুলির সাধারণ পরিণতি সনাক্ত করতে মেমোয়েজ করুন। বা আরও ভাল, একবারে 8 টি ট্রেলিং বিট দেখুন (জ্ঞানারের উত্তর)
@Eof নির্দেশ করে যে tzcnt
(বা bsf
) n/=2
এক ধাপে একাধিক পুনরাবৃত্তি করতে ব্যবহৃত হতে পারে । এটি সম্ভবত সিমডি ভেক্টরাইজিংয়ের চেয়ে ভাল; কোনও এসএসই বা AVX নির্দেশনা এটি করতে পারে না। এটি এখনও n
বিভিন্ন পূর্ণসংখ্যার নিবন্ধগুলিতে সমান্তরালে একাধিক স্কেলারগুলি করার সাথে সামঞ্জস্যপূর্ণ ।
সুতরাং লুপটি দেখতে এটি দেখতে পারে:
goto loop_entry; // C++ structured like the asm, for illustration only
do {
n = n*3 + 1;
loop_entry:
shift = _tzcnt_u64(n);
n >>= shift;
count += shift;
} while(n != 1);
এটি উল্লেখযোগ্যভাবে কম পুনরাবৃত্তি করতে পারে, তবে ভেরিয়েবল-কাউন্টের শিফট BMI2 ছাড়াই ইন্টেল এসএনবি-পরিবার সিপিইউগুলিতে ধীর হয়। 3 উফ, 2 সি ল্যাটেন্সি (এফএএলজিএসে তাদের একটি ইনপুট নির্ভরতা রয়েছে কারণ গণনা = 0 এর অর্থ পতাকাগুলি সংশোধিত নয় They তারা এটিকে ডেটা নির্ভরতা হিসাবে পরিচালনা করে এবং একাধিক উফ গ্রহণ করে কারণ একটি উওপটিতে কেবল 2 ইনপুট থাকতে পারে (যাইহোক প্রাক-এইচএসডাব্লু / বিডিডাব্লু))। X86 এর পাগল-সিআইএসসি নকশার বিষয়ে লোকেরা অভিযোগ করার বিষয়টি উল্লেখ করছে। এটি x86 সিপিইউগুলিকে তাদের চেয়ে ধীর করে তোলে যদি আজ আইএসএ স্ক্র্যাচ থেকে তৈরি করা হয়েছিল এমনকি এমনকি বেশিরভাগ ক্ষেত্রে similar (অর্থাত্ এটি "x86 ট্যাক্স" এর অংশ যা গতি / শক্তি ব্যয় করে SH) SHRX / SHLX / SARX (BMI2) একটি বড় জয় (1 টি ইউওপ / 1 সি ল্যাটেন্সি)।
এটি tzcnt (হাসওয়েলের উপর 3c এবং পরবর্তীকালে) সমালোচনামূলক পথে ফেলেছে, সুতরাং এটি লুপ বহনকারী নির্ভরতা শৃঙ্খলের মোট বিলম্বকে উল্লেখযোগ্যভাবে দীর্ঘায়িত করে। n>>1
যদিও এটি কোনও সিএমওভের জন্য, বা একটি রেজিস্ট্রেশন হোল্ডিং প্রস্তুত করার জন্য কোনও প্রয়োজন সরিয়ে দেয় । @ ভিড্রাকের উত্তর একাধিক পুনরাবৃত্তির জন্য tzcnt / শিফট স্থগিত করে এগুলি কাটিয়ে উঠেছে, যা অত্যন্ত কার্যকর (নীচে দেখুন)।
আমরা নিরাপদে BSF বা TZCNT বিনিময়যোগ্যভাবে ব্যবহার করতে পারি , কারণ n
এই মুহুর্তে কখনই শূন্য হতে পারে না। বিএমআই 1 সমর্থন করে না এমন সিপিইউগুলিতে টিজেডিসিএনটির মেশিন-কোডটি বিএসএফ হিসাবে ডিকোড করে। (অর্থহীন উপসর্গগুলি উপেক্ষা করা হয়, তাই আরইপি বিএসএফ বিএসএফ হিসাবে চালিত হয়)।
টিজেডিসিএনটি এটি সমর্থনকারী এএমডি সিপিইউগুলিতে বিএসএফের চেয়ে অনেক বেশি ভাল সম্পাদন করে, তাই REP BSF
আউটপুটের পরিবর্তে ইনপুট শূন্য হলে আপনি জেডএফ সেট করার বিষয়ে চিন্তা না করলেও এটি ব্যবহার করা ভাল ধারণা হতে পারে । কিছু সংকলক যখন আপনি __builtin_ctzll
এমনকি ব্যবহার করেন তখন এটি করেন -mno-bmi
।
তারা ইন্টেল সিপিইউগুলিতে একই সম্পাদন করে, তাই কেবলমাত্র বাইটটি সংরক্ষণ করুন যদি এটি গুরুত্বপূর্ণ। ইনটেলের টিজেডিসিএনটি (প্রাক-স্কাইলেক) এখনও বিএসএফের মতো অনুমিত রাইটিং-আউটপুট অপারেন্ডের উপর একটি মিথ্যা-নির্ভরতা রয়েছে যা ইনপুট = 0 দ্বারা নির্বিঘ্নিত বিএসএফ তার গন্তব্যটিকে অবিচ্ছিন্ন ছেড়ে দেয় support সুতরাং আপনাকে কেবলমাত্র স্কাইলেকে অনুকূলকরণ না করাতে সেদিকেই কাজ করা উচিত, তাই অতিরিক্ত আরইপি বাইট থেকে লাভের কিছুই নেই। (ইন্টেল প্রায়শই x86 আইএসএ ম্যানুয়াল যা প্রয়োজন তার উপর নির্ভর করে এবং এর বাইরে চলে যায়, যা ব্যবহার করা উচিত নয় এমন কোনও বিষয়ের উপর নির্ভর করে বা এটি প্রত্যাখ্যানজনকভাবে বাতিল নয় eg যেমন উইন্ডোজ 9 এক্স এর টিএলবি এন্ট্রিগুলির কোনও অনুমানমূলক প্রিফেচিং ধরে নেই , যা নিরাপদ ছিল কোডটি যখন লেখা হয়েছিল, তার আগে ইনটেল টিএলবি পরিচালনার নিয়ম আপডেট করেছিল ))
যাইহোক, হাসওয়েলের এলজেডিসিএনটি / টিজেডিসিএনটির পিওপিসিএনটি-র মতো একই মিথ্যা ডিপ রয়েছে: এই প্রশ্নোত্তর দেখুন । এই কারণেই @ ভিড্রাকের কোডের জন্য জিসিসির এসএম আউটপুটে আপনি দেখতে পাচ্ছেন যে এটি রেজিস্টারটিতে জোর-শূন্যের সাথে ডিপ চেইনটি ভাঙ্গা হবে যখন এটি ডিএসটি = এসসিআর ব্যবহার না করে TZCNT এর গন্তব্য হিসাবে ব্যবহার করবে। যেহেতু টিজেডিসএনটি / এলজেডিসিএনটি / পিওপিসিএনটি তাদের গন্তব্যটিকে কখনই সংজ্ঞায়িত বা অপরিবর্তিত রেখে দেয় না, তাই ইন্টেল সিপিইউতে আউটপুটের উপর এই মিথ্যা নির্ভরতা একটি পারফরম্যান্স বাগ / সীমাবদ্ধতা। সম্ভবত কিছু ট্রানজিস্টর / ক্ষমতা একই মূল্য নির্ধারণের ইউনিটে যাওয়ার মতো অন্যান্য উফদের মতো আচরণ করার জন্য এটি মূল্যবান। একমাত্র পারফিউডের উল্টোটি হ'ল অন্য উড়ানের সীমাবদ্ধতার সাথে মিথস্ক্রিয়া: তারা কোনও সূচিযুক্ত ঠিকানা মোডের সাহায্যে মেমরি অপারেণ্ডকে মাইক্রো-ফিউজ করতে পারে they হাসওলে, তবে স্কাইলেকে যেখানে ইন্টেল এলজেডিসিএনটি / টিজেডিসিএনটির জন্য মিথ্যা ডেপ সরিয়েছে তারা পিএনপিসিএনটি এখনও কোনও অ্যাডার মোডকে মাইক্রো-ফিউজ করতে পারে এমনদিকে তারা "আন-ল্যামিনেট" ইডেক্সিং অ্যাড্রেসিং মোডগুলি ফেলেছে।
অন্যান্য উত্তর থেকে ধারণা / কোডে উন্নতি:
@ হিডফ্র্যামকজিবি এর উত্তরে একটি সুন্দর পর্যবেক্ষণ রয়েছে যে আপনি 3n + 1 এর পরে একটি ডান শিফট করতে সক্ষম হওয়ার গ্যারান্টিযুক্ত। আপনি এটিকে আরও কার্যকরভাবে গণনা করতে পারেন কেবল পদক্ষেপের মধ্যে চেক না রেখে। এই উত্তরে asm বাস্তবায়ন ভেঙে গেছে, যদিও (এটি OF এর উপর নির্ভর করে, যা একটি গণনা> 1 এর সাথে SHRD এর পরে সংজ্ঞায়িত) এবং ধীর: এর ROR rdi,2
চেয়ে দ্রুততর SHRD rdi,rdi,2
এবং সমালোচনামূলক পথে দুটি সিএমওভি নির্দেশাবলী ব্যবহার করা একটি অতিরিক্ত টেস্টের চেয়ে ধীর যে সমান্তরাল চলতে পারে।
আমি পরিপাটি / উন্নত সি রেখেছি (যা সংকলককে আরও ভাল asm উত্পাদন করতে গাইড করে) এবং + গডবোল্টে আরও দ্রুত asm (সি এর নীচের মন্তব্যে) কাজ করে পরীক্ষা করেছি: @ হাইডফ্র্যামকজিবি এর উত্তরের লিঙ্কটি দেখুন । (এই উত্তরটি বৃহত গডবোল্ট ইউআরএলগুলি থেকে 30k চর সীমাতে আঘাত করে তবে শর্টলিঙ্কগুলি পচতে পারে এবং যাইহোক goo.gl এর জন্য খুব দীর্ঘ ছিল))
স্ট্রিংতে রূপান্তর করতে এবং write()
একবারে চার লেখার পরিবর্তে একটি তৈরি করতে আউটপুট-মুদ্রণকে আরও উন্নত করে । এটি পুরো কর্মসূচির সময় নির্ধারণের ক্ষেত্রে perf stat ./collatz
(পারফরম্যান্স কাউন্টারগুলি রেকর্ড করার জন্য) প্রভাবকে হ্রাস করে এবং আমি কিছু অ-সমালোচক এএসএমকে অবহেলা করেছিলাম।
@ Veedrac এর কোড
ডান স্থানান্তর থেকে আমাদের যতটা প্রয়োজন জানা এবং লুপটি চালিয়ে যাওয়ার জন্য চেক করা হয়েছে তার থেকে আমি একটি সামান্য গতিপথ পেয়েছি । কোরের 2 ডুও (মেরোম) এ 16 এর আনারল ফ্যাক্টর সহ সীমা = 1e8 কমিয়ে 7.25 সেকেন্ডে।
কোড + গডবোল্ট সম্পর্কে মন্তব্য । ঝাঁকুনি সহ এই সংস্করণটি ব্যবহার করবেন না; এটি ডিফার-লুপের সাথে নির্বোধ কিছু করে। একটি টিএমপি কাউন্টার ব্যবহার k
করে এবং count
পরে এটিকে যুক্ত করা পরে কী ঝাঁকুনি করে তা পরিবর্তন করে তবে এতে জিসিসি কিছুটা ব্যথা করে।
মন্তব্য আলোচনা দেখুন: Veedrac এর কোড হল চমৎকার BMI1 (অর্থাত সেলেরন না / পেন্টিয়াম) সঙ্গে সিপিইউ উপর