হাতের লিখিত সমাবেশের চেয়ে কোলাটজ অনুমানটি দ্রুত পরীক্ষার জন্য সি ++ কোড - কেন?


832

আমি এই দুটি সমাধান প্রজেক্ট অয়লার কিউ 14 এর জন্য , সমাবেশে এবং সি ++ এ লিখেছি । কোলাটজ অনুমানের পরীক্ষার জন্য এগুলি একই অভিন্ন বৌদ্ধ শক্তি পদ্ধতির । সমাবেশ সমাধান একত্রিত হয়েছিল

nasm -felf64 p14.asm && gcc p14.o -o p14

সি ++ এর সাথে সংকলিত হয়েছিল

g++ p14.cpp -o p14

অ্যাসেম্বলি p14.asm

section .data
    fmt db "%d", 10, 0

global main
extern printf

section .text

main:
    mov rcx, 1000000
    xor rdi, rdi        ; max i
    xor rsi, rsi        ; i

l1:
    dec rcx
    xor r10, r10        ; count
    mov rax, rcx

l2:
    test rax, 1
    jpe even

    mov rbx, 3
    mul rbx
    inc rax
    jmp c1

even:
    mov rbx, 2
    xor rdx, rdx
    div rbx

c1:
    inc r10
    cmp rax, 1
    jne l2

    cmp rdi, r10
    cmovl rdi, r10
    cmovl rsi, rcx

    cmp rcx, 2
    jne l1

    mov rdi, fmt
    xor rax, rax
    call printf
    ret

সি ++, পি 14 সি পি পি

#include <iostream>

using namespace std;

int sequence(long n) {
    int count = 1;
    while (n != 1) {
        if (n % 2 == 0)
            n /= 2;
        else
            n = n*3 + 1;

        ++count;
    }

    return count;
}

int main() {
    int max = 0, maxi;
    for (int i = 999999; i > 0; --i) {
        int s = sequence(i);
        if (s > max) {
            max = s;
            maxi = i;
        }
    }

    cout << maxi << endl;
}

আমি গতি এবং সমস্ত উন্নতির জন্য সংকলক অপ্টিমাইজেশানগুলি সম্পর্কে জানি, তবে আমার সমাবেশ সমাধানটি আরও অনুকূল করার অনেক উপায় আমি দেখতে পাই না (প্রোগ্রামিকভাবে গাণিতিকভাবে নয়) speaking

সি ++ কোডে প্রতিটি শব্দ এবং বিভাজন প্রতি পদে মডিউল থাকে, যেখানে সমাবেশ এমনকি প্রতিটি পদে মাত্র একটি বিভাগ থাকে।

তবে সমাবেশটি সি ++ সমাধানের চেয়ে গড়ে 1 সেকেন্ড বেশি সময় নিচ্ছে। কেন? আমি প্রধানত কৌতূহল জিজ্ঞাসা করছি।

ফাঁসির সময়

আমার সিস্টেম: 1.4 গিগাহার্টজ ইন্টেল সেলারন 2955U (হাসওয়েল মাইক্রোআরকিটেকচার) এ 64 বিট লিনাক্স।


232
আপনি কি জি সি সি আপনার সি ++ প্রোগ্রামের জন্য সমাবেশের কোডটি পরীক্ষা করেছেন?
রুখ

69
সংকলকটি -Sযে সমাবেশটি তৈরি করেছিল তা পেতে কম্পাইল করুন । সংকলকটি উপলব্ধি করতে যথেষ্ট স্মার্ট যে মডুলাস একই সময়ে বিভাগ করে।
ব্যবহারকারী 3386109

267
আমি মনে করি আপনার অপশন আছে 1. তোমার পরিমাপ কৌশল ত্রুটিপূর্ণ হয়, 2. কম্পাইলার ভাল সমাবেশ লিখেছেন যে আপনি, বা 3. কম্পাইলার ব্যবহার জাদু।
গালিক


18
@ জেফারসন এই সংকলকটি দ্রুত ব্রুট ফোর্স ব্যবহার করতে পারে। উদাহরণস্বরূপ সম্ভবত এসএসই নির্দেশাবলী সহ।
ব্যবহারকারী 253751

উত্তর:


1896

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

দক্ষ asm কীভাবে লিখতে হয় তা শিখতে Agner Fog এর অপ্টিমাইজিং অ্যাসেমব্লিক গাইডটি দেখুন । নির্দিষ্ট সিপিইউগুলির জন্য সুনির্দিষ্ট বিবরণের জন্য তার নির্দেশাবলী টেবিল এবং একটি মাইক্রোয়ার্ক গাইডও রয়েছে। এছাড়াও দেখুন আরও পারফেক্ট লিঙ্কের জন্য ট্যাগ উইকি।

হাতে লিখিত 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 r329 টি উওস, 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পরিবর্তে ব্যবহার করতে পারি ।testcmovz

 ### 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মান পৌঁছে যাওয়া পর্যন্ত অপেক্ষা করা উচিত কিনা , অথবা অন্য ক্রমের জন্য রেজিস্টারগুলিকে স্পর্শ না করে শেষ শর্তে পৌঁছানো মাত্র একটির জন্য একটি নতুন সূচনা পয়েন্ট পাওয়া যায় কিনা। সম্ভবত প্রতিটি চেইন দরকারী ডেটাতে কাজ করা সবচেয়ে ভাল, অন্যথায় আপনাকে শর্তসাপেক্ষে এর পাল্টা বাড়িয়ে তুলতে হবে।1n


এমনকি আপনি এসএসই প্যাকড-তুলনা স্টাফ দিয়ে শর্তসাপেক্ষে ভেক্টর উপাদানগুলির জন্য কাউন্টারকে বাড়িয়ে তুলতে পারেন যেখানে এখনও 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 (অর্থাত সেলেরন না / পেন্টিয়াম) সঙ্গে সিপিইউ উপর


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

3
@EOF: না, আমি ভেতরের লুপ খুঁজে ভঙ্গ বোঝানো যখন কোন এক ভেক্টর উপাদান হিট 1পরিবর্তে তারা সব আছে (PCMPEQ / PMOVMSK সঙ্গে সহজে নির্ধারণযোগ্য)। তারপরে আপনি PINSRQ এবং স্টাফ ব্যবহার করে এমন এক উপাদানকে টলমল করতে (এবং এর কাউন্টারগুলি) রেখে লুপটিতে ফিরে যান। আপনি খুব সহজেই অভ্যন্তরীণ লুপটি প্রায়শই ভেঙে ফেললে এটি সহজেই ক্ষতির মধ্যে পরিণত হতে পারে তবে এর অর্থ এই যে আপনি সর্বদা অভ্যন্তরীণ লুপের প্রতিটি পুনরাবৃত্তিটি দরকারী কাজের 2 বা 4 উপাদান পান getting স্মৃতিচারণ সম্পর্কে ভাল পয়েন্ট, যদিও।
পিটার কর্ডেস

4
@ জেফারসন সেরা পরিচালনা করেছেন Godbolt.org/g/1N70Ib । আমি আশা করছিলাম যে আমি স্মার্ট কিছু করতে পারি, তবে তা মনে হয় না।
Veedrac

86
এটি যেমন অবিশ্বাস্য উত্তর সম্পর্কে আমাকে অবাক করে দেয় তা হল এই ধরণের বিশদটি দেখানো জ্ঞান। আমি কখনই সেই স্তরটিতে কোনও ভাষা বা সিস্টেম জানব না এবং আমি কীভাবে তা জানতাম না। স্যার ভাল করেছেন স্যার।
ক্যামডেন_কিড

8
কিংবদন্তি উত্তর !!
সুমিত জৈন 10

104

সি ++ সংকলক সক্ষম সংসদীয় ভাষা প্রোগ্রামারের তুলনায় আরও অনুকূল কোড তৈরি করতে পারে বলে দাবি করা খুব খারাপ ভুল। এবং বিশেষত এই ক্ষেত্রে। মানব সবসময় সংকলক যে কোডটি করতে পারে তা আরও উন্নত করতে পারে এবং এই বিশেষ পরিস্থিতিটি এই দাবির ভাল চিত্রণ।

আপনি যে সময়সীমার পার্থক্যটি দেখছেন তা হ'ল কারণ প্রশ্নটির সমাবেশ কোডটি অভ্যন্তরীণ লুপগুলিতে অনুকূল থেকে খুব দূরে।

(নীচের কোডটি 32-বিট, তবে সহজেই 64-বিটে রূপান্তরিত হতে পারে)

উদাহরণস্বরূপ, সিকোয়েন্স ফাংশনটি কেবলমাত্র 5 টি নির্দেশকে অনুকূলিত করা যেতে পারে:

    .seq:
        inc     esi                 ; counter
        lea     edx, [3*eax+1]      ; edx = 3*n+1
        shr     eax, 1              ; eax = n/2
        cmovc   eax, edx            ; if CF eax = edx
        jnz     .seq                ; jmp if n<>1

পুরো কোডটি দেখে মনে হচ্ছে:

include "%lib%/freshlib.inc"
@BinaryType console, compact
options.DebugMode = 1
include "%lib%/freshlib.asm"

start:
        InitializeAll
        mov ecx, 999999
        xor edi, edi        ; max
        xor ebx, ebx        ; max i

    .main_loop:

        xor     esi, esi
        mov     eax, ecx

    .seq:
        inc     esi                 ; counter
        lea     edx, [3*eax+1]      ; edx = 3*n+1
        shr     eax, 1              ; eax = n/2
        cmovc   eax, edx            ; if CF eax = edx
        jnz     .seq                ; jmp if n<>1

        cmp     edi, esi
        cmovb   edi, esi
        cmovb   ebx, ecx

        dec     ecx
        jnz     .main_loop

        OutputValue "Max sequence: ", edi, 10, -1
        OutputValue "Max index: ", ebx, 10, -1

        FinalizeAll
        stdcall TerminateAll, 0

এই কোডটি সংকলন করার জন্য, ফ্রেশলিব প্রয়োজন।

আমার পরীক্ষায় (1 গিগাহার্টজ এএমডি এ 4-1200 প্রসেসর), উপরের কোডটি প্রশ্ন থেকে সি ++ কোডের চেয়ে প্রায় চারগুণ দ্রুত (যখন সংকলন করা হয়েছে -O0: 430 এমএস বনাম 1900 এমএস) এবং দ্বিগুণেরও বেশি দ্রুত (430) এমএস বনাম 830 এমএস) যখন সি ++ কোডটি সংকলিত হয় -O3

উভয় প্রোগ্রামের আউটপুট একই: i = 837799 এ সর্বোচ্চ সিকোয়েন্স = 525।


6
হুঁ, এটা চতুর। এসএআরআর কেবলমাত্র ইএএক্স 1 (বা 0) হলে জেডএফ সেট করে। আমি এটা মিস করেছি যে যখন সিসিটির -O3আউটপুট অনুকূলিত হয়েছিল , তবে আমি আপনার অভ্যন্তরীণ লুপে তৈরি অন্যান্য সমস্ত অপ্টিমাইজেশনকে স্পষ্ট করেছিলাম । (তবে আপনি কেন আইএনসির পরিবর্তে কাউন্টার ইনক্রিমেন্টের জন্য এলইএ ব্যবহার করবেন? এই মুহুর্তে পতাকা আঁটসাঁট করা ঠিক আছে, এবং সম্ভবত পি 4 বাদে কোনও কিছুতেই মন্দা দেখা দিতে পারে (আইএনসি এবং এসএইচআর উভয়ের জন্য পুরানো পতাকাগুলির উপর মিথ্যা নির্ভরতা)। এলইএ করতে পারে ' টি যতগুলি বন্দর চালনা করে না এবং সংস্থানীয় সংঘাতকে আরও ঘন ঘন সমালোচনামূলক পথে বিলম্বিত করতে পারে))
পিটার কর্ডেস

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

95
" সি ++ সংকলককে আরও ভাল দাবি করা খুব খারাপ ভুল And এবং বিশেষত এই ক্ষেত্রে human মানব সর্বদা কোডটিকে আরও ভাল করে তুলতে পারে যে এবং এই বিশেষ সমস্যাটি এই দাবির ভাল চিত্রণ। " আপনি এটিকে বিপরীত করতে পারেন এবং এটি ঠিক যেমন বৈধ হবে । " একজন মানুষের দাবি করা ভাল, এটি খুব খারাপ ভুল And এবং বিশেষত এই ক্ষেত্রে human মানব সর্বদা কোডটিকে আরও খারাপ করতে পারে যে এবং এই বিশেষ প্রশ্নটি এই দাবির ভাল চিত্র illust " সুতরাং আমি মনে করি না এখানে আপনার কোনও বক্তব্য আছে , এই জাতীয়করণগুলি ভুল।
luk32

5
@ luk32 - তবে প্রশ্নটির লেখক মোটেই কোনও যুক্তি হতে পারে না, কারণ তার সমাবেশ ভাষা সম্পর্কে জ্ঞান শূন্যের কাছাকাছি। মানব বনাম সংকলক সম্পর্কে প্রতিটি যুক্তি অন্তত কিছুটা মাঝারি স্তরের asm জ্ঞানের সাথে অন্তর্নিহিতভাবে ধরে নেয়। আরও: উপপাদ্য "মানব লিখিত কোড সর্বদা উন্নত বা সংকলক উত্পন্ন কোডের সমান হবে" আনুষ্ঠানিকভাবে প্রমাণিত হওয়া খুব সহজ।
জনফাউন্ড

30
@ লুক 32: একটি দক্ষ মানুষ সংকলক আউটপুট দিয়ে শুরু করতে পারে (এবং সাধারণত হওয়া উচিত)। সুতরাং যতক্ষণ আপনি আপনার প্রয়াসটি সত্যই দ্রুততর হয়েছে (আপনার যে টার্গেট হার্ডওয়্যারটির জন্য আপনি টিউন করছেন) তা নিশ্চিত করার জন্য যতক্ষণ আপনি বেঞ্চমার্ক করবেন ততক্ষণ আপনি সংকলকটির চেয়ে খারাপ কিছু করতে পারবেন না। তবে হ্যাঁ, আমাকে একমত হতে হবে এটি কিছুটা দৃ strong় বক্তব্য। সংকলকগণ সাধারণত আভিজাত্য asm কোডারগুলির চেয়ে অনেক ভাল করে। তবে সাধারণত যে সংকলকগুলি আসে তার তুলনায় কোনও নির্দেশিকা বা দুটি সংরক্ষণ করা সম্ভব। (সর্বদা উড়ানের উপর নির্ভর করে সমালোচনামূলক পথে নয়)। এগুলি জটিল যন্ত্রপাতিগুলির জন্য অত্যন্ত দরকারী টুকরা, তবে তারা "স্মার্ট" নয়।
পিটার কর্ডেস

24

আরও পারফরম্যান্সের জন্য: একটি সাধারণ পরিবর্তন পর্যবেক্ষণ করছে যে n = 3n + 1 এর পরে n সমান হবে, তাই আপনি অবিলম্বে 2 দ্বারা ভাগ করতে পারেন। এবং এন 1 হবে না, সুতরাং এটির জন্য আপনাকে পরীক্ষা করার দরকার নেই। সুতরাং আপনি বিবৃতি লিখতে এবং লিখতে কিছু সংরক্ষণ করতে পারে:

while (n % 2 == 0) n /= 2;
if (n > 1) for (;;) {
    n = (3*n + 1) / 2;
    if (n % 2 == 0) {
        do n /= 2; while (n % 2 == 0);
        if (n == 1) break;
    }
}

এখানে একটি বড় জয়: আপনি যদি সর্বনিম্ন 8 টি বিটের দিকে নজর রাখেন, আপনি 2 টি আট বার ভাগ না করা পর্যন্ত সমস্ত পদক্ষেপগুলি এই আটটি বিট দ্বারা সম্পূর্ণ নির্ধারিত হয়। উদাহরণস্বরূপ, শেষ আটটি বিট যদি 0x01 হয় তবে এটি বাইনারিতে আপনার নম্বরটি ???? 0000 0001 এরপরে পরবর্তী পদক্ষেপগুলি হ'ল:

3n+1 -> ???? 0000 0100
/ 2  -> ???? ?000 0010
/ 2  -> ???? ??00 0001
3n+1 -> ???? ??00 0100
/ 2  -> ???? ???0 0010
/ 2  -> ???? ???? 0001
3n+1 -> ???? ???? 0100
/ 2  -> ???? ???? ?010
/ 2  -> ???? ???? ??01
3n+1 -> ???? ???? ??00
/ 2  -> ???? ???? ???0
/ 2  -> ???? ???? ????

সুতরাং এই সমস্ত পদক্ষেপের পূর্বাভাস দেওয়া যেতে পারে, এবং 256k + 1 81k + 1 দিয়ে প্রতিস্থাপন করা হয়েছে যা সমস্ত সংমিশ্রণের জন্য অনুরূপ কিছু ঘটবে। সুতরাং আপনি একটি বড় সুইচ বিবৃতি দিয়ে একটি লুপ করতে পারেন:

k = n / 256;
m = n % 256;

switch (m) {
    case 0: n = 1 * k + 0; break;
    case 1: n = 81 * k + 1; break; 
    case 2: n = 81 * k + 1; break; 
    ...
    case 155: n = 729 * k + 425; break;
    ...
}

N ≤ 128 অবধি লুপটি চালান, কারণ সেই সময়ে n 2 দ্বারা 8 টিরও কম বিভাগের সাথে 1 হয়ে উঠতে পারে এবং একসাথে আট বা তার বেশি পদক্ষেপ করা আপনাকে প্রথম স্থানটিতে পৌঁছানোর বিন্দুটি মিস করবে make তারপরে "সাধারণ" লুপটি চালিয়ে যান - বা একটি টেবিল প্রস্তুত করুন যা আপনাকে জানায় যে আরও 1 টি পৌঁছানোর আরও কত ধাপ দরকার।

পুনশ্চ. আমি দৃ strongly়ভাবে সন্দেহ করি যে পিটার কর্ডসের পরামর্শ এটিকে আরও দ্রুততর করবে। একটি ব্যতীত কোনও শর্তাধীন শাখা থাকবে না এবং লুপটি আসলে শেষ হওয়া ব্যতীত এটিকে সঠিকভাবে পূর্বাভাস দেওয়া হবে। কোড কিছু হবে

static const unsigned int multipliers [256] = { ... }
static const unsigned int adders [256] = { ... }

while (n > 128) {
    size_t lastBits = n % 256;
    n = (n >> 8) * multipliers [lastBits] + adders [lastBits];
}

অনুশীলনে, আপনি একবারে শেষ 9, 10, 11, 12 বিট প্রসেসিং দ্রুততর হবে কিনা তা পরিমাপ করবেন। প্রতিটি বিটের জন্য, টেবিলের প্রবেশের সংখ্যা দ্বিগুণ হবে এবং আমি যখন টেবিলগুলি L1 ক্যাশে ফিট না করে তখন আমি মন্দা ছাড়িয়ে যাব।

PPS। আপনার যদি অপারেশনগুলির সংখ্যা প্রয়োজন হয়: প্রতিটি পুনরাবৃত্তিতে আমরা ঠিক দুটি দ্বারা আটটি বিভাগ করি, এবং (3n + 1) অপারেশনগুলির একটি পরিবর্তনশীল সংখ্যা করি, সুতরাং অপারেশনগুলি গণনা করার জন্য একটি স্পষ্ট পদ্ধতি অন্য অ্যারে হবে। তবে আমরা আসলে পদক্ষেপের সংখ্যা গণনা করতে পারি (লুপটির পুনরাবৃত্তির সংখ্যার ভিত্তিতে)।

আমরা সমস্যাটিকে কিছুটা নতুনভাবে সংজ্ঞায়িত করতে পারি: n টির সাথে (3n + 1) / 2 টি বিজোড় করে প্রতিস্থাপন করুন এবং n / 2 এর সাথে এন এমনকি প্রতিস্থাপন করুন। তারপরে প্রতিটি পুনরাবৃত্তি হ'ল 8 টি পদক্ষেপ করবে তবে আপনি সেই প্রতারণার বিষয়টি বিবেচনা করতে পারেন :-) সুতরাং ধরুন সেখানে r অপারেশনগুলি ছিল <<- 3n + 1 এবং s অপারেশন n <- n / 2। ফলাফলটি ঠিক হ'ল এন '= এন * 3 ^ আর / 2 ^ গুলি হবে, কারণ এন <- 3 এন + 1 অর্থ এন <- 3 এন * (1 + 1/3 এন)। লোগারিদম গ্রহণের সাথে আমরা r = (গুলি + লগ 2 (এন '/ এন)) / লগ 2 (3) পাই।

আমরা যদি এন ≤ ১,০০,০০০ অবধি লুপটি করি এবং কোনও প্রারম্ভিক বিন্দু থেকে ite ১,০০,০০০ পর্যন্ত কতগুলি পুনরুক্তি প্রয়োজন তার পূর্বনির্ধারিত সারণী থাকে তবে উপরের হিসাবে r গণনা করা নিকটতম পূর্ণসংখ্যার সাথে গোল করে সঠিক ফলাফল প্রদান করবে যদি না সত্যই বড় হয়।


2
বা গুনের জন্য ডেটা লকিং টেবিলগুলি তৈরি করুন এবং একটি স্যুইচের পরিবর্তে ধ্রুবক যুক্ত করুন। দুটি 256-এন্ট্রি সারণী সূচী করা একটি জাম্প টেবিলের চেয়ে দ্রুত এবং সংকলকরা সম্ভবত সেই রূপান্তরটি খুঁজছেন না।
পিটার কর্ডেস

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

আপডেট করতে count, আপনার তৃতীয় অ্যারে দরকার, তাই না? adders[]কতগুলি ডান-শিফট করা হয়েছিল তা আপনাকে জানায় না।
পিটার কর্ডেস

বৃহত্তর টেবিলগুলির জন্য, ক্যাশে ঘনত্ব বাড়ানোর জন্য সংকীর্ণ প্রকারগুলি ব্যবহার করা উপযুক্ত। বেশিরভাগ আর্কিটেকচারে, একটি থেকে শূন্য-প্রসারিত লোড uint16_tখুব সস্তা। X86, এটা জিরো-ব্যাপ্ত 32 বিট থেকে শুধু সস্তা হিসাবে unsigned intথেকে uint64_t। (ইনটেল সিপিইউতে থাকা মেমোরি থেকে মোভেজএক্সএক্সের জন্য কেবল একটি লোড-পোর্ট ইউওপ প্রয়োজন, তবে এএমডি সিপিইউগুলিতেও ALU প্রয়োজন)) ওহে বিটিডাব্লু, আপনি কেন ব্যবহার size_tকরছেন lastBits? এটি একটি 32-বিট টাইপযুক্ত -m32এবং এমনকি -mx32(32-বিট পয়েন্টার সহ দীর্ঘ মোড)। এটি অবশ্যই ভুল ধরণের জন্য n। শুধু ব্যবহার unsigned
পিটার কর্ডেস

20

বরং সম্পর্কিত সম্পর্কিত নোটে: আরও পারফরম্যান্স হ্যাক!

  • [প্রথম "অনুমান" অবশেষে @ShreevatsaR দ্বারা প্রকাশিত হয়েছে; মুছে]

  • সিকোয়েন্সটি ট্র্যাভার করার সময়, আমরা কেবলমাত্র বর্তমান উপাদানটির 2-পাড়াতে 3 টি সম্ভাব্য কেস পেতে পারি N(প্রথমে দেখানো হয়েছে):

    1. [এমনকি] [বিজোড়]
    2. [বিজোড়] [এমনকি]
    3. [এমনকি] [এমনকি]

    গত গনা এই উপাদান 2 উপায়ে লিপ করতে (N >> 1) + N + 1, ((N << 1) + N + 1) >> 1এবং N >> 2যথাক্রমে।

    আসুন প্রমাণ করুন যে উভয় ক্ষেত্রে (1) এবং (2) প্রথম সূত্র ব্যবহার করা সম্ভব (N >> 1) + N + 1,।

    কেস (1) সুস্পষ্ট। কেস (২) এর দ্বারা বোঝা যায় (N & 1) == 1, সুতরাং আমরা যদি ধরে নিই (সাধারণতার ক্ষতি ছাড়াই) এন 2-বিট লম্বা এবং এর বিটগুলি baসর্বাধিক থেকে কমপক্ষে-তাত্পর্যপূর্ণ হয় a = 1, এবং নিম্নলিখিতটি ধারণ করে:

    (N << 1) + N + 1:     (N >> 1) + N + 1:
    
            b10                    b1
             b1                     b
           +  1                   + 1
           ----                   ---
           bBb0                   bBb

    যেখানে B = !b। প্রথম ফলাফলটি ডান স্থানান্তর করা আমাদের ঠিক যা চায় তা দেয়।

    Qed: (N & 1) == 1 ⇒ (N >> 1) + N + 1 == ((N << 1) + N + 1) >> 1

    প্রমাণিত হিসাবে, আমরা একক ত্রৈমাসিক ক্রিয়াকলাপটি ব্যবহার করে একযোগে সিকোয়েন্স 2 উপাদানগুলি অতিক্রম করতে পারি। আরও 2 × সময় হ্রাস।

ফলস্বরূপ অ্যালগরিদম এর মতো দেখাচ্ছে:

uint64_t sequence(uint64_t size, uint64_t *path) {
    uint64_t n, i, c, maxi = 0, maxc = 0;

    for (n = i = (size - 1) | 1; i > 2; n = i -= 2) {
        c = 2;
        while ((n = ((n & 3)? (n >> 1) + n + 1 : (n >> 2))) > 2)
            c += 2;
        if (n == 2)
            c++;
        if (c > maxc) {
            maxi = i;
            maxc = c;
        }
    }
    *path = maxc;
    return maxi;
}

int main() {
    uint64_t maxi, maxc;

    maxi = sequence(1000000, &maxc);
    printf("%llu, %llu\n", maxi, maxc);
    return 0;
}

এখানে আমরা তুলনা করি n > 2কারণ ক্রমের মোট দৈর্ঘ্যটি বিজোড় হলে প্রক্রিয়াটি 1 এর পরিবর্তে 2 এ থামতে পারে।

[Edit:]

এর সমাবেশে এটি অনুবাদ করুন!

MOV RCX, 1000000;



DEC RCX;
AND RCX, -2;
XOR RAX, RAX;
MOV RBX, RAX;

@main:
  XOR RSI, RSI;
  LEA RDI, [RCX + 1];

  @loop:
    ADD RSI, 2;
    LEA RDX, [RDI + RDI*2 + 2];
    SHR RDX, 1;
    SHRD RDI, RDI, 2;    ror rdi,2   would do the same thing
    CMOVL RDI, RDX;      Note that SHRD leaves OF = undefined with count>1, and this doesn't work on all CPUs.
    CMOVS RDI, RDX;
    CMP RDI, 2;
  JA @loop;

  LEA RDX, [RSI + 1];
  CMOVE RSI, RDX;

  CMP RAX, RSI;
  CMOVB RAX, RSI;
  CMOVB RBX, RCX;

  SUB RCX, 2;
JA @main;



MOV RDI, RCX;
ADD RCX, 10;
PUSH RDI;
PUSH RCX;

@itoa:
  XOR RDX, RDX;
  DIV RCX;
  ADD RDX, '0';
  PUSH RDX;
  TEST RAX, RAX;
JNE @itoa;

  PUSH RCX;
  LEA RAX, [RBX + 1];
  TEST RBX, RBX;
  MOV RBX, RDI;
JNE @itoa;

POP RCX;
INC RDI;
MOV RDX, RDI;

@outp:
  MOV RSI, RSP;
  MOV RAX, RDI;
  SYSCALL;
  POP RAX;
  TEST RAX, RAX;
JNE @outp;

LEA RAX, [RDI + 59];
DEC RDI;
SYSCALL;

সংকলনের জন্য এই আদেশগুলি ব্যবহার করুন:

nasm -f elf64 file.asm
ld -o file file.o

গডবোল্টে পিটার কর্ডেস কর্তৃক এএসএমটির সি এবং উন্নত / বাগফিক্সড সংস্করণ দেখুন । (সম্পাদক এর নোট: আপনার উত্তরে আমার জিনিস রাখার জন্য দুঃখিত, কিন্তু আমার উত্তর গডবোল্ট লিঙ্কগুলি + পাঠ্য থেকে 30k চর সীমাতে আঘাত করেছে!)


2
এর Qমতো কোনও অবিচ্ছেদ্য নেই 12 = 3Q + 1। আপনার প্রথম কথাটি সঠিক নয়, মিথথিক্স।
Veedrac

1
@ উইড্রাক: এটির সাথে খেলা হয়েছে: আরওআর / টেস্ট এবং কেবলমাত্র একটি সিএমওভ ব্যবহার করে, এই উত্তরের প্রয়োগের চেয়ে এটি আরও ভাল asm দিয়ে প্রয়োগ করা যেতে পারে। এই এসএম কোডটি আমার সিপিইউতে অসীম লুপগুলি, যেহেতু এটি স্পষ্টতই অফ এর উপর নির্ভর করে, যা এসআরডিডি বা আরওআর পরে গণনার সাথে সংজ্ঞায়িত হয়> ১. এটি বর্ধিতভাবে mov reg, imm32বাইটগুলি সংরক্ষণ করার জন্য এড়াতে চেষ্টা করতেও অনেক দীর্ঘায়িত হয় , তবে তারপরে এটি ব্যবহার করে এমনকি সর্বত্র register৪-বিট সংস্করণের নিবন্ধের সংস্করণটিতে এর জন্য xor rax, raxপ্রচুর অপ্রয়োজনীয় আরএক্স উপসর্গ রয়েছে। nঅতিরিক্ত প্রবাহ এড়াতে আমাদের স্পষ্টতই কেবল অভ্যন্তরীণ লুপে থাকা রেগগুলিতে রেক্স দরকার ।
পিটার কর্ডেস

1
সময় ফলাফল (একটি কোর 2 ডুও ই 6600 থেকে: মেরম ২.৪ গিগাহার্জ। কমপ্লেক্স-এলইএ = 1 সি ল্যাটেন্সি, সিএমওভ = 2 সি) । সেরা একক পদক্ষেপ asm অভ্যন্তরীণ-লুপ বাস্তবায়ন (জনফাউন্ড থেকে): এই @ মেইন লুপের রান প্রতি 111ms। আমার এই সি-এর ডি-ওবসফেসেটেড সংস্করণ থেকে সংকলক আউটপুট (কিছু টিএমপি ওয়ার্স -O3 -march=core2সহ ): ক্লাঙ্গ 3.3 : 96 মিমি ms gcc5.2: 108ms। আমার ঝাঁকুনির asm অভ্যন্তরীণ লুপের উন্নত সংস্করণ থেকে: 92 মিমি (এসএনবি-পরিবারে আরও বড় উন্নতি হওয়া উচিত, যেখানে জটিল এলইএ 3 সি 1 সি নয়)। আমার এই asm লুপটির উন্নত + কার্যকারী সংস্করণ থেকে (ROR + TEST ব্যবহার করে, SHRD নয়): 87ms। মুদ্রণের আগে 5 টি reps সহ পরিমাপ করা হয়েছে
পিটার কর্ডেস

2
এখানে প্রথম 66 রেকর্ড-সেটার রয়েছে (ওআইআইএস এ A006877); আমি এমনকি জোড়গুলিকে জোরে চিহ্নিত করেছি: 2, 3, 6, 7, 9, 18, 25, 27, 54, 73, 97, 129, 171, 231, 313, 327, 649, 703, 871, 1161, 2223, 2463, 2919, 3711, 6171, 10971, 13255, 17647, 23529, 26623, 34239, 35655, 52527, 77031, 106239, 142587, 156159, 216367, 230631, 410011, 511935, 626331, 1601, 11170 1723519, 2298025, 3064033, 3542887, 3732423, 5649499, 6649279, 8400511, 11200681, 14934241, 15733191, 31466382, 36791535, 63728127, 127456254, 169941673, 226588897, 268549803, 537099606, 670617279, 1341234558
ShreevatsaR

1
@ hidefromkgb দুর্দান্ত! এবং আমি এখন আপনার অন্যান্য বিষয়টির আরও প্রশংসা করি: 4 কে + 2 → 2 কে + 1 → 6 কে + 4 = (4 কে + 2) + (2 কে + 1) + 1, এবং 2 কে + 1 → কে 4 4 → 3 কে + 2 = ( 2 কে + 1) + (কে) + 1. চমৎকার পর্যবেক্ষণ!
শ্রীভাতসার

6

সোর্স কোড থেকে মেশিন কোড তৈরির সময় সি ++ প্রোগ্রামগুলি সমাবেশ প্রোগ্রামগুলিতে অনুবাদ করা হয় to এটি বলা কার্যত ভুল হবে যে সমাবেশটি সি ++ এর চেয়ে ধীর। তদতিরিক্ত, বাইনারি কোড উত্পন্ন সংকলক থেকে সংকলক থেকে পৃথক। সুতরাং একটি স্মার্ট সি ++ সংকলক বোবা এসেম্বলারের কোডের চেয়ে বাইনারি কোডটি আরও অনুকূল এবং দক্ষ তৈরি করতে পারে।

তবে আমি বিশ্বাস করি আপনার প্রোফাইলিং পদ্ধতিতে কিছু ত্রুটি রয়েছে। নিম্নলিখিতটি প্রোফাইলিংয়ের জন্য সাধারণ নির্দেশিকা:

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

হ্যাঁ আমি কোনও আনুষ্ঠানিক প্রোফাইলিং করি নি তবে আমি উভয়কে কয়েকবার চালিয়েছি এবং 3 সেকেন্ড থেকে 2 সেকেন্ড বলতে সক্ষম। যাইহোক উত্তর দেওয়ার জন্য ধন্যবাদ। আমি ইতিমধ্যে এখানে খুব ভাল তথ্য
নিয়েছি

9
এটা সম্ভবত না শুধু একটি পরিমাপ ত্রুটি, হাতে লেখা এ এস এম কোড একটি ডান-শিফট পরিবর্তে একটি 64-বিট DIV নির্দেশ ব্যবহার করছে। আমার উত্তর দেখুন। তবে হ্যাঁ, সঠিকভাবে পরিমাপ করাও গুরুত্বপূর্ণ।
পিটার কর্ডেস

7
বুলেট পয়েন্টগুলি কোনও কোড ব্লকের চেয়ে উপযুক্ত ফর্ম্যাটিং। দয়া করে আপনার পাঠ্যকে একটি কোড ব্লকে স্থাপন করা বন্ধ করুন, কারণ এটি কোড নয় এবং কোনও মনসপ্যাসেড ফন্টের দ্বারা উপকৃত হয় না।
পিটার কর্ডেস

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

6

কোলাটজ সমস্যার জন্য, আপনি "লেজগুলি" ক্যাশে করে পারফরম্যান্সে একটি উল্লেখযোগ্য উত্সাহ পেতে পারেন। এটি একটি সময় / মেমরি ট্রেড অফ। দেখুন: স্মৃতিচারণ ( https://en.wikedia.org/wiki/ মেমোমাইজেশন )। আপনি অন্যান্য সময় / মেমরি ট্রেড-অফগুলির জন্য ডায়নামিক প্রোগ্রামিং সমাধানগুলিও দেখতে পারেন।

অজগর বাস্তবায়ন উদাহরণ:

import sys

inner_loop = 0

def collatz_sequence(N, cache):
    global inner_loop

    l = [ ]
    stop = False
    n = N

    tails = [ ]

    while not stop:
        inner_loop += 1
        tmp = n
        l.append(n)
        if n <= 1:
            stop = True  
        elif n in cache:
            stop = True
        elif n % 2:
            n = 3*n + 1
        else:
            n = n // 2
        tails.append((tmp, len(l)))

    for key, offset in tails:
        if not key in cache:
            cache[key] = l[offset:]

    return l

def gen_sequence(l, cache):
    for elem in l:
        yield elem
        if elem in cache:
            yield from gen_sequence(cache[elem], cache)
            raise StopIteration

if __name__ == "__main__":
    le_cache = {}

    for n in range(1, 4711, 5):
        l = collatz_sequence(n, le_cache)
        print("{}: {}".format(n, len(list(gen_sequence(l, le_cache)))))

    print("inner_loop = {}".format(inner_loop))

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

2
যদি আমি উপরের দিক দিয়ে জ্ঞানারের ধারণাটি সঠিকভাবে বুঝতে পারি তবে আমি মনে করি লেজ মেমোইজেশনটি একটি অর্থোগোনাল অপ্টিমাইজেশন। সুতরাং আপনি উভয়ই বোধগম্য করতে পারেন। জ্ঞানারের অ্যালগরিদমে মেমোয়েজেশন যোগ করা থেকে আপনি কতটা অর্জন করতে পারবেন তা তদন্ত করা আকর্ষণীয় হবে।
ইমানুয়েল ল্যান্ডহোম

2
আমরা কেবল ফলাফলগুলির ঘন অংশটি সংরক্ষণ করে মেমোয়েজেশনকে সস্তা করে তুলতে পারি। N এর উপরের সীমা নির্ধারণ করুন এবং তারও বেশি, মেমরি পরীক্ষা করে দেখুন। তার নীচে হ্যাশ ফাংশন হিসাবে হ্যাশ (এন) -> এন ব্যবহার করুন, সুতরাং কী = অ্যারেতে অবস্থান করুন এবং এটি সঞ্চয় করার দরকার নেই। একটি এন্ট্রি 0এখনও উপস্থিত না। আমরা কেবলমাত্র টেবিলে বিজোড় এন সংরক্ষণকারী, তাই হ্যাশ ফাংশন আরো অপ্টিমাইজ করতে পারেন n>>1, এর পদক্ষেপ কোড খারিজ 1. লেখা সবসময় একটি দিয়ে শেষ n>>tzcnt(n)বা কিছু এটা বিশ্রী নিশ্চিত করতে।
পিটার কর্ডেস

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

2
আপনি কিছু বৃহত্তর এন এর জন্য সমস্ত এন <এন এর জন্য প্রাক-গণিত ফলাফলগুলি সঞ্চয় করতে পারেন So সুতরাং আপনার কোনও হ্যাশ টেবিলের ওভারহেডের দরকার নেই। সেই টেবিলের ডেটা প্রতিটি প্রারম্ভিক মানের জন্য শেষ পর্যন্ত ব্যবহৃত হবে । যদি আপনি কেবল নিশ্চিত করতে চান যে কোলাটজ ক্রমটি সর্বদা (1, 4, 2, 1, 4, 2, ...) এ শেষ হয়: এটি এন> 1 এর প্রমাণের সমতুল্য প্রমাণিত হতে পারে, ক্রমটি শেষ পর্যন্ত হবে মূল এন থেকে কম হতে হবে। এবং তার জন্য, ক্যাশেিং লেজগুলি কোনও সহায়তা করবে না।
gnasher729

5

মন্তব্য থেকে:

তবে, এই কোডটি কখনও থামে না (সংখ্যার ওভারফ্লো কারণে)!?! ইয়ভেস দাউস্ট

অনেক সংখ্যার জন্য এটি উপচে পড়বে না

যদি হবে ওভারফ্লো - যারা হতভাগ্য প্রাথমিক বীজ একটির জন্য, overflown সংখ্যা খুব সম্ভবত অন্য ওভারফ্লো ছাড়া 1 দিকে মিলিত হবে।

তবুও এটি আকর্ষণীয় প্রশ্ন তোলে, কিছু ওভারফ্লো-চক্রীয় বীজ সংখ্যা আছে?

যে কোনও সাধারণ চূড়ান্ত রূপান্তরকারী সিরিজ দুটি মানের পাওয়ার (যথেষ্ট সুস্পষ্ট?) দিয়ে শুরু হয়।

2 ^ 64 শূন্যে উপচে যাবে, যা অ্যালগরিদম অনুসারে অপরিজ্ঞাত অসীম লুপ (কেবলমাত্র 1 দিয়ে শেষ হয়), তবে shr raxজেডএফ = 1 উত্পাদনের কারণে উত্তরের সর্বাধিক অনুকূল সমাধান শেষ হবে ।

আমরা 2 ^ 64 উত্পাদন করতে পারি? যদি শুরুর সংখ্যাটি হয় তবে 0x5555555555555555এটি বিজোড় সংখ্যা, পরের সংখ্যাটি 3n + 1 হয়, যা 0xFFFFFFFFFFFFFFFF + 1= 0। তাত্ত্বিকভাবে অ্যালগরিদমের অপরিজ্ঞাত অবস্থায়, তবে জনফাউন্ডের অনুকূলিত উত্তরটি জেডএফ = 1 এ উপস্থিত হয়ে পুনরুদ্ধার করবে। cmp rax,1পিটার Cordes এর অসীম লুপ এর মধ্যে সমাপ্ত হবে (Qed বৈকল্পিক 1, অনির্ধারিত মাধ্যমে "cheapo" 0নম্বর)।

কীভাবে আরও কিছু জটিল সংখ্যা, যা চক্র ছাড়াই তৈরি করবে 0? সত্যই, আমি নিশ্চিত নই, আমার গণিতের তত্ত্বটি কোনও গুরুতর ধারণা পেতে খুব বিরক্তিকর, কীভাবে এটি গুরুতর উপায়ে মোকাবেলা করতে হবে। তবে স্বজ্ঞাতভাবে আমি বলব যে সিরিজটি প্রতিটি সংখ্যার জন্য 1 তে রূপান্তরিত হবে: 0 <সংখ্যার, 3n + 1 সূত্রটি ধীরে ধীরে অরিজিনাল সংখ্যার (বা মধ্যবর্তী) প্রতিটি অ 2-প্রধান মৌলিকটিকে 2 এর শক্তিতে পরিণত করবে, তাড়াতাড়ি বা পরে । সুতরাং আমাদের মূল সিরিজের জন্য অসীম লুপ সম্পর্কে চিন্তা করার দরকার নেই, কেবল উপচে পড়া আমাদের বাধা দিতে পারে।

সুতরাং আমি শীটে কয়েকটি সংখ্যা রেখেছি এবং 8 টি বিট কাটা সংখ্যার দিকে একবার দেখেছি।

থেকে সজল তিন মান আছে 0: 227, 170এবং 85( 85সরাসরি যাচ্ছে 0, অন্য দুটি দিকে অগ্রগতি লাভ 85)।

তবে চক্রাকার ওভারফ্লো বীজ তৈরির কোনও মূল্য নেই।

মজাদারভাবে যথেষ্ট পরিমাণে আমি একটি চেক করেছিলাম, যা 8 বিট কাটছাঁটে প্রথম সমস্যায় পড়ে এবং ইতিমধ্যে 27এটি আক্রান্ত হয়েছে! এটি 9232যথাযথ নন-কেটে যাওয়া সিরিজের মান পৌঁছে দেয় (প্রথম কাটা মানটি 322দ্বাদশ ধাপে রয়েছে), এবং ছাঁটাই-বিহীন উপায়ে 2-255 ইনপুট সংখ্যার যে কোনওটির কাছে পৌঁছানো সর্বাধিক মান হল 13120( 255নিজেই), সর্বোচ্চ পদক্ষেপের সংখ্যা রূপান্তরকরণ 1প্রায় 128(+ -2, "1" গণনা করা হয় কিনা তা নিশ্চিত নয়, ইত্যাদি ...)।

আকর্ষণীয়ভাবে যথেষ্ট (আমার জন্য) সংখ্যাটি 9232অন্যান্য বহু উত্সের সংখ্যার পক্ষে সর্বাধিক, এ সম্পর্কে কী বিশেষ? : -ও 9232= 0x2410... হুমমম .. ধারণা নেই।

দুর্ভাগ্যবশত আমি এই সিরিজের কোনো গভীর উপলব্ধি করতে পেতে পারেন, কেন এটা মিলিত না এবং তাদের ছিন্ন করা প্রভাব কি কি k বিট, কিন্তু cmp number,1সসীম অবস্থা এটা অবশ্যই বিশেষ ইনপুট মান হিসাবে বিভক্তি সঙ্গে অসীম লুপ মধ্যে অ্যালগরিদম করা সম্ভব 0পর ছাঁটাই।

তবে 278 বিট কেসের জন্য ভরাট করা মানটি হ'ল সতর্কতা, এটি দেখে মনে হচ্ছে আপনি যদি মান পর্যন্ত পৌঁছানোর পদক্ষেপের সংখ্যা গণনা করেন 1তবে মোট সংখ্যার কে-বিট সংখ্যার সংখ্যা থেকে সংখ্যাগরিষ্ঠের জন্য আপনি ভুল ফল পাবেন। 8 বিটের পূর্ণসংখ্যার জন্য 256 এর মধ্যে 146 সংখ্যা ট্র্যাঙ্কেশন দ্বারা সিরিজকে প্রভাবিত করেছে (তাদের মধ্যে কিছু এখনও দুর্ঘটনার দ্বারা সঠিক ধাপে আঘাত করতে পারে, আমি পরীক্ষা করতে খুব অলস))


"ওভারফ্লাউন সংখ্যাটি সম্ভবত খুব বেশি অন্য প্রবাহ ছাড়াই 1 এর দিকে রূপান্তর করবে": কোডটি কখনও থামে না। (এটি একটি অনুমান যেহেতু আমি নিশ্চিত হওয়ার জন্য সময় শেষ হওয়া পর্যন্ত অপেক্ষা করতে পারি না ...)
ইয়ভেস দাউস্ট

@ ইয়ভেডউউস্ট ওহ, তবে এটি কি করে? ... উদাহরণস্বরূপ 278 বি ট্রানসেশন সহ সিরিজটি এরকম দেখাচ্ছে: 82 41 124 62 31 94 47 142 71 214 107 66 (কাটা) 33 100 50 25 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 (এর বাকি অংশগুলি কাটা ছাড়াই কাজ করে)। দুঃখিত, আমি তোমাকে পাই না কেটে যাওয়া মানটি বর্তমানে চলমান সিরিজের আগের কিছুটির সমান হলে এটি কখনই থামবে না এবং আমি কে-বিট কাটছাঁটির তুলনায় এমন কোনও মান খুঁজে পাই না (তবে আমি পিছনে ম্যাথ থিওরিটি বের করতে পারি না, কেন এটি 8/16/32/64 বিট বিচ্ছিন্নকরণের জন্য ধারণ করে, কেবল স্বজ্ঞাতভাবে আমি মনে করি এটি কার্যকর হয়)।
পেড 7 জি

1
আমার প্রাথমিক সমস্যার বিবরণ শীঘ্রই পরীক্ষা করা উচিত ছিল: "যদিও এটি এখনও প্রমাণিত হয়নি (কোলাটজ সমস্যা), মনে করা হয় যে সমস্ত শুরুর সংখ্যা 1-এ শেষ হবে।" ... ঠিক আছে, আশ্চর্যের কিছু নেই আমি আমার সীমিত অস্পষ্ট গণিত জ্ঞান তা উপলব্ধি করতে পেতে পারেন ...: D: এবং আমার চাদর পরীক্ষা থেকে আমি তোমাদের আশ্বস্ত করতে প্রতি জন্য মিলিত করে 2- 255পারেন ছাঁটাই ছাড়া (থেকে, সংখ্যা 1), বা 8 বিট ছাঁটাই সহ (হয় প্রত্যাশিত 1বা 0তিনটি সংখ্যার জন্য)।
পেড 7 জি

হেম, যখন আমি বলি যে এটি কখনই থামে না, আমি বলতে চাইছি ... এটি বন্ধ হয় না। আপনি যদি চান তবে প্রদত্ত কোডটি চিরতরে চলে।
ইয়ভেস দাউস্ট

1
ওভারফ্লোতে কী ঘটে তা বিশ্লেষণের জন্য উত্সাহিত। সিএমপি-ভিত্তিক লুপটি শূন্যে অবসান করতে cmp rax,1 / jna(অর্থাত্ do{}while(n>1)) ব্যবহার করতে পারে । nআমরা কতটা ওভারফ্লোতে যেতে পারি তার একটি ধারণা দিতে লুপটির একটি চালিত সংস্করণ তৈরি করার কথা ভেবেছিলাম যা সর্বাধিক দেখা রেকর্ড করে ।
পিটার কর্ডেস

5

আপনি সংকলকটির দ্বারা উত্পন্ন কোডটি পোস্ট করেননি, সুতরাং এখানে কিছু অনুমানক রয়েছে, তবে এটি না দেখেও কেউ বলতে পারেন যে এটি:

test rax, 1
jpe even

... শাখার ভুল ধারণা করার 50% সম্ভাবনা রয়েছে এবং এটি ব্যয়বহুল হবে।

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


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

5

এমনকি সমাবেশ না দেখেও সর্বাধিক স্পষ্ট কারণ /= 2হ'ল সম্ভবত এটি অনুকূলিত হয়েছে >>=1এবং অনেক প্রসেসরের খুব শিফট অপারেশন রয়েছে। তবে প্রসেসরের শিফট অপারেশন না থাকলেও ভাসমান পয়েন্ট বিভাগের চেয়ে পূর্ণসংখ্যা বিভাগ দ্রুত হয়।

সম্পাদনা করুন: আপনার মিলটি উপরের "ভাসমান পয়েন্ট বিভাগের চেয়ে পূর্ণসংখ্যা বিভাগ দ্রুত" স্টেটমেন্টে পৃথক হতে পারে। নীচের মন্তব্যগুলি প্রকাশ করে যে আধুনিক প্রসেসরগুলি পূর্ণসংখ্যা বিভাগের তুলনায় এফপি বিভাগকে অনুকূলকরণ করে prior সুতরাং কেউ যদি এই থ্রেডের প্রশ্নটি সম্পর্কে জিজ্ঞাসা করে যে গতিরোধের সর্বাধিক কারণের সন্ধান করছেন, তবে সংকলকটি সর্বোত্তম দেখার জন্য সেরা 1 ম স্থান /=2হিসাবে অনুকূলিতকরণ >>=1করবে।


একটি অন সম্পর্কহীন নোট , যদি nবিজোড় প্রকাশের n*3+1সবসময় এমনকি হতে হবে। সুতরাং চেক করার দরকার নেই। আপনি এই শাখাটি পরিবর্তন করতে পারেন

{
   n = (n*3+1) >> 1;
   count += 2;
}

পুরো বিবৃতিটি তখন হবে

if (n & 1)
{
    n = (n*3 + 1) >> 1;
    count += 2;
}
else
{
    n >>= 1;
    ++count;
}

4
আধুনিক x86 সিপিইউগুলিতে পূর্ণসংখ্যা বিভাগ এফপি বিভাগের চেয়ে আসলে দ্রুত নয়। আমি মনে করি এটি ইনটেল / এএমডি তাদের এফপি বিভাজকের উপর আরও বেশি ট্রানজিস্টর ব্যয় করার কারণে হয়েছে কারণ এটি আরও গুরুত্বপূর্ণ একটি অপারেশন। (ধ্রুবক দ্বারা পূর্ণসংখ্যা বিভাগ একটি মডুলার বিপরীত দ্বারা গুণিত করতে অনুকূলিত করা যেতে পারে)। অ্যাগনার ফগের ইনসন টেবিলগুলি পরীক্ষা করে দেখুন এবং ডিআইভিএসডি (ডাবল-স্পষ্টতা ফ্লোট) DIV r32(32-বিট স্বাক্ষরবিহীন পূর্ণসংখ্যার) বা DIV r64(অনেক ধীর 64৪ -বিট স্বাক্ষরবিহীন পূর্ণসংখ্যার) সাথে তুলনা করুন । বিশেষত থ্রুপুটটির জন্য, এফপি বিভাগটি আরও দ্রুত (মাইক্রো কোডডের পরিবর্তে একক উওফ এবং আংশিক পাইপলাইনযুক্ত), তবে বিলম্বিতা আরও ভাল।
পিটার কর্ডেস

1
যেমন ওপি'র হাসওয়েল সিপিইউতে: ডিআইভিএসডি হ'ল 1 ইউওপ, 10-20 চক্রের বিলম্বিতা, প্রতি 8-14c থ্রুপুট প্রতি এক। div r6436 উওপস, 32-96 সি ল্যাটেন্সি এবং 21-74c থ্রুপুট প্রতি এক। স্কাইলেকে আরও দ্রুত এফপি বিভাগ থ্রুপুট রয়েছে (আরও ভাল লেটেন্সি না দিয়ে প্রতি 4 সি প্রতি একটিতে পাইপলাইনযুক্ত), তবে খুব দ্রুত পূর্ণসংখ্যার ডিভ নেই। বিষয়গুলি এএমডি বুলডোজার-পরিবারে একই রকম: ডিআইভিএসডি হ'ল 1 এম-ওপ, 9-27 সি ল্যাটেন্সি, প্রতি 4.5-11c থ্রুটপুট প্রতি এক। div r6416 এম-অপস, 16-75c লম্বা, প্রতি 16-75c থ্রুপুট।
পিটার কর্ডেস

1
এফপি বিভাগটি কি মূলত পূর্ণসংখ্যার-বিয়োগকারী এক্সটোন্টস, পূর্ণসংখ্যার-বিভাজন ম্যান্টিসার, ডেনোরমালগুলি সনাক্ত করে? এবং এই 3 টি পদক্ষেপ সমান্তরালভাবে করা যেতে পারে।
এমসাল্টারস

2
@ এসএমএলটাররা: হ্যাঁ, এটি ঠিক শোনাচ্ছে তবে শেষের দিকে সাধারণকরণের ধাপের সাথে ঘনিষ্ঠ এবং ম্যান্টিসের মধ্যে বিট বিট। doubleএকটি 53-বিট ম্যান্টিসা আছে, তবে এটি হাসওলের চেয়ে এখনও উল্লেখযোগ্যভাবে ধীর div r32। সুতরাং সমস্যাটি হার্ডওয়ার ইন্টেল / এএমডি কতটা ফেলে দেয় তা অবশ্যই একটি বিষয়, কারণ তারা পূর্ণসংখ্যা এবং এফপি উভয় ক্ষেত্রেই একই ট্রানজিস্টর ব্যবহার করে না। পূর্ণসংখ্যাটি একটি স্কেলার (কোনও পূর্ণসংখ্যার সিমড বিভাজন নেই), এবং ভেক্টর একটি 128 বি ভেক্টর পরিচালনা করে (256b অন্যান্য ভেক্টর এর মতো নয়)। বড় কথা হ'ল পূর্ণসংখ্যা ডিভি হ'ল অনেক উফ, আশেপাশের কোডগুলিতে বড় প্রভাব।
পিটার কর্ডেস

ত্রুটি, ম্যান্টিসা এবং এক্সপোনেন্টের মধ্যে বিট শিফট না করে, ম্যান্টিসাকে একটি শিফট দিয়ে সাধারণ করুন, এবং শিফ্টের পরিমাণটি এক্সপোনেন্টে যুক্ত করুন।
পিটার কর্ডেস

4

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

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

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


2
সম্পূর্ণরূপে এটির সাথে একমত। gcc -O3সেই সঠিক অ্যালগরিদমের জন্য কোড তৈরি করা হয়েছে যা হাসওয়েলে সর্বোত্তম 20% এর মধ্যে ছিল। (যাদের speedups পথ আমার উত্তর প্রধান ফোকাস ছিল শুধুমাত্র কারণ কি প্রশ্ন জিজ্ঞাসা যে, এবং একটি আকর্ষণীয় উত্তর আছে, না কারণ এটি সঠিক পন্থা আছে।) অনেক বড় speedups রূপান্তরের থেকে প্রাপ্ত হয়েছে কম্পাইলার অত্যন্ত সন্ধান করার সম্ভাবনা কম হবে যেমন ডান শিফট পিছিয়ে দেওয়া, বা একবারে ২ টি পদক্ষেপ করা। এর চেয়ে বড় স্পিডআপগুলি মেমোয়েজেশন / লকউইচ-টেবিলগুলি থেকে পাওয়া যেতে পারে। এখনও নিখুঁত পরীক্ষা, কিন্তু খাঁটি উদ্দীপনা নয়।
পিটার কর্ডেস

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

-2

সহজ উত্তর:

  • একটি এমওভি আরবিএক্স, 3 এবং মুল আরবিএক্স করা ব্যয়বহুল; শুধু আরবিএক্স, দুবার আরবিএক্স যোগ করুন

  • ADD 1 সম্ভবত এখানে INC এর চেয়ে দ্রুত

  • এমওভি 2 এবং ডিআইভি খুব ব্যয়বহুল; শুধু ডান স্থানান্তর

  • -৪-বিট কোডটি সাধারণত 32-বিট কোডের চেয়ে কম ধীরে ধীরে হয় এবং প্রান্তিককরণের সমস্যাগুলি আরও জটিল হয়; এই জাতীয় ছোট প্রোগ্রামের সাথে আপনাকে সেগুলি প্যাক করতে হবে যাতে আপনি 32-বিট কোডের চেয়ে দ্রুত হওয়ার কোনও সম্ভাবনা পাওয়ার জন্য সমান্তরাল গণনা করছেন

আপনি যদি আপনার সি ++ প্রোগ্রামের জন্য সমাবেশ তালিকা উত্পন্ন করেন তবে আপনি দেখতে পাবেন এটি কীভাবে আপনার সমাবেশ থেকে আলাদা।


4
1): এলইএর তুলনায় 3 বার যোগ করা বোবা হবে। এছাড়াও mul rbxওপির হাসওয়েল সিপিইউতে 3 সি ল্যাটেন্সি সহ 2 উফ (এবং প্রতি ক্লক থ্রুপুট 1 টি) রয়েছে। imul rcx, rbx, 3একই 3 সি ল্যাটেন্সি সহ কেবল 1 টি ইউওপ। দুটি এডিডির নির্দেশাবলী 2 সি ল্যাটেন্সি সহ 2 উফ হবে।
পিটার কর্ডস

5
2) এডিডি 1 সম্ভবত এখানে আইএনসির চেয়ে দ্রুতনাহ, ওপি পেন্টিয়াম 4 ব্যবহার করছে না । আপনার পয়েন্ট 3) এই উত্তরের একমাত্র সঠিক অংশ।
পিটার কর্ডেস

5
4) মোট বাজে কথা মত। পয়েন্টার-ভারী ডেটা স্ট্রাকচারের সাহায্যে 64৪-বিট কোডটি ধীর হতে পারে, কারণ বৃহত্তর পয়েন্টার মানে বড় ক্যাশে পাদদেশ। তবে এই কোডটি কেবল রেজিস্টারে কাজ করছে এবং কোড অ্যালাইনমেন্ট সংক্রান্ত সমস্যাগুলি 32 এবং 64 বিট মোডে একই। (সুতরাং ডেটা সারিবদ্ধকরণের সমস্যাগুলি, x86-64 এর জন্য প্রান্তিককরণটি বড় সমস্যা হওয়ার সাথে আপনি কী বলছেন সে সম্পর্কে কোনও ধারণা নেই)। যাইহোক, কোড এমনকি লুপ ভিতরে মেমরি স্পর্শ করে না।
পিটার কর্ডস

মন্তব্যকারী কোন বিষয়ে কথা বলছেন সে সম্পর্কে কোনও ধারণা নেই। 64৪-বিট সিপিইউতে একটি এমওভি + এমএল করুন আপনার নিজের সাথে দু'বার নিবন্ধ যুক্ত করার চেয়ে প্রায় তিনগুণ কম হবে। তাঁর অন্যান্য মন্তব্যও সমানভাবে ভুল।
টাইলার ডারডেন

6
ওয়েল এমওভি + এমএলইউ অবশ্যই বোবা, তবে এমওভি + অ্যাড + এডিডি এখনও নিখুঁত (আসলে ADD RBX, RBXদু'বার করা 4 এর সাথে গুণিত হবে, 3 নয়)। এখন পর্যন্ত সবচেয়ে ভাল উপায় lea rax, [rbx + rbx*2]। অথবা, এটি একটি 3-উপাদান এলইএ তৈরির ব্যয়ে, +1 করুন lea rax, [rbx + rbx*2 + 1] (1 এর পরিবর্তে এইচএসডাব্লুতে 3 সি ল্যাটেন্সি, যেমন আমি আমার উত্তরে ব্যাখ্যা করেছি) আমার বক্তব্যটি ছিল যে 64৪-বিট গুণটি খুব ব্যয়বহুল নয় সাম্প্রতিক ইন্টেল CPU- র, তারা ঝটপটভাবে দ্রুত আছে কারণ সংখ্যাবৃদ্ধি ইউনিট (এমনকি এএমডির, যেখানে একই সঙ্গে তুলনা পূর্ণসংখ্যা MUL r644c প্রতি 6c লেটেন্সি হয়, এক সঙ্গে থ্রুপুট। এমনকি সম্পূর্ণরূপে pipelined না
পিটার Cordes
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.