ফলাফলটি যাই হোক না কেন দ্রুততম পূর্ণসংখ্যা বিভাগ শূন্য দ্বারা বিভাগকে সমর্থন করে?


109

সারসংক্ষেপ:

আমি গণনার দ্রুততম উপায় খুঁজছি

(int) x / (int) y

ব্যতিক্রম না পেয়ে y==0। পরিবর্তে আমি কেবল একটি স্বেচ্ছাসেবী ফলাফল চাই।


পটভূমি:

চিত্র প্রক্রিয়াকরণ অ্যালগরিদমগুলি কোডিং করার সময় আমার প্রায়শই একটি (জমে থাকা) আলফা মান দ্বারা ভাগ করা প্রয়োজন। সবচেয়ে সহজ রূপটি হল পূর্ণসংখ্যার পাটিগণিত সহ প্লেইন সি কোড। আমার সমস্যাটি হ'ল আমি সাধারণত ফলাফল পিক্সেলের জন্য শূন্য ত্রুটির দ্বারা বিভাগ পাই alpha==0। তবে এটি হুবহু পিক্সেল যেখানে ফলাফল মোটেই গুরুত্বপূর্ণ নয়: আমি পিক্সেলগুলির রঙের মানগুলি নিয়ে যত্ন করি না alpha==0


বিবরণ:

আমি এরকম কিছু খুঁজছি:

result = (y==0)? 0 : x/y;

অথবা

result = x / MAX( y, 1 );

x এবং y হল ধনাত্মক পূর্ণসংখ্যা। কোডটি নেস্টেড লুপে বিপুল সংখ্যক বার কার্যকর করা হয়, তাই আমি শর্তযুক্ত শাখা থেকে মুক্তি পাওয়ার উপায় খুঁজছি।

যখন y বাইট সীমা অতিক্রম করবেন না, আমি সমাধানটিতে খুশি

unsigned char kill_zero_table[256] = { 1, 1, 2, 3, 4, 5, 6, 7, [...] 255 };
[...]
result = x / kill_zero_table[y];

তবে এটি অবশ্যই বড় রেঞ্জগুলির পক্ষে ভাল কাজ করে না।

আমি চূড়ান্ত প্রশ্নটি অনুমান করি: অন্যান্য সমস্ত মান অপরিবর্তিত রেখে, অন্য কোন পূর্ণসংখ্যার মানকে 0 পরিবর্তন করে দ্রুততম বিট টুইডলিং হ্যাকটি কী?


ব্যাখ্যা

আমি 100% নিশ্চিত নই যে শাখা প্রশস্ত করা খুব ব্যয়বহুল। তবে, বিভিন্ন সংকলক ব্যবহৃত হয়, তাই আমি সামান্য অপটিমাইজেশন (যা প্রকৃতপক্ষে সন্দেহজনক) এর সাথে বেঞ্চমার্কিং পছন্দ করি।

অবশ্যই, সংযোজকগুলি বিট টুইডলিংয়ের ক্ষেত্রে দুর্দান্ত তবে আমি সি এর "যত্ন নেই" ফলাফলটি প্রকাশ করতে পারি না, তাই সংকলকটি কখনই সম্পূর্ণরূপীকরণের অনুকূলতাটি ব্যবহার করতে সক্ষম হবে না।

কোডটি পুরো সি সি সামঞ্জস্যপূর্ণ হওয়া উচিত, প্রধান প্ল্যাটফর্মগুলি লিনাক্স 64৪ বিট, জিসিসি এবং ক্লাঙ্গ এবং ম্যাকোস সহ।


22
আপনি কীভাবে নির্ধারণ করেছেন যে যদি শাখাটি খুব ব্যয়বহুল হয়?
djechlin

7
আপনি কিভাবে নির্ধারণ করেছি যে সেখানে নেই একটি শাখা?
লীমস

13
প্রোফাইলিংয়ের জন্য +1, আধুনিক দিনের শাখার পূর্বাভাস সহ আপনার এটি প্রয়োজন হতে পারে না। এছাড়াও, আপনি কেন নিজের ইমেজ প্রসেসিং অ্যালগরিদমগুলিকে কোডিং করছেন?
টিসি 1

8
"দ্রুততম বিট টুইডলিং হ্যাক কী ..." হতে পারে y += !y? এটি গণনা করার জন্য কোনও শাখার দরকার নেই। আপনি তুলনা পারে x / (y + !y)বিরুদ্ধে x / max(y, 1)এবং হয়ত এছাড়াও y ? (x/y) : 0। আমার ধারণা কমপক্ষে অপ্টিমাইজেশন চালু থাকলে এগুলির কোনওটিতেই কোনও শাখা থাকবে না।
লীমস

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

উত্তর:


107

কিছু মন্তব্যে অনুপ্রাণিত হয়ে আমি আমার পেন্টিয়ামের শাখা থেকে মুক্তি পেয়েছি এবং gccব্যবহার করে সংকলক ব্যবহার করেছি

int f (int x, int y)
{
        y += y == 0;
        return x/y;
}

সংকলকটি মূলত স্বীকৃতি দেয় যে এটি সংযোজনে পরীক্ষার শর্তের পতাকা ব্যবহার করতে পারে।

অনুরোধ অনুযায়ী সমাবেশ:

.globl f
    .type   f, @function
f:
    pushl   %ebp
    xorl    %eax, %eax
    movl    %esp, %ebp
    movl    12(%ebp), %edx
    testl   %edx, %edx
    sete    %al
    addl    %edx, %eax
    movl    8(%ebp), %edx
    movl    %eax, %ecx
    popl    %ebp
    movl    %edx, %eax
    sarl    $31, %edx
    idivl   %ecx
    ret

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

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

ফিল্মের এআরএম সংকলক সহ সংস্করণ:

f PROC
        CMP      r1,#0
        BNE      __aeabi_idivmod
        MOVEQ    r0,#0
        BX       lr

জিসিসির সাথে ফিলিপের সংস্করণ:

f:
        subs    r3, r1, #0
        str     lr, [sp, #-4]!
        moveq   r0, r3
        ldreq   pc, [sp], #4
        bl      __divsi3
        ldr     pc, [sp], #4

এআরএম সংকলক সহ আমার কোড:

f PROC
        RSBS     r2,r1,#1
        MOVCC    r2,#0
        ADD      r1,r1,r2
        B        __aeabi_idivmod

জিসিসির সাথে আমার কোড:

f:
        str     lr, [sp, #-4]!
        cmp     r1, #0
        addeq   r1, r1, #1
        bl      __divsi3
        ldr     pc, [sp], #4

সমস্ত সংস্করণে বিভাগের রুটিনে এখনও একটি শাখা দরকার, কারণ এআরএমের এই সংস্করণটিতে একটি বিভাগের জন্য হার্ডওয়্যার নেই, তবে এর জন্য পরীক্ষাটি y == 0পূর্বাভাসিত প্রয়োগের মাধ্যমে পুরোপুরি বাস্তবায়িত হয়।


আপনি কি আমাদের ফলাফল এসেম্বলারের কোডটি প্রদর্শন করতে পারেন? বা কীভাবে আপনি নির্ধারণ করেছিলেন যে কোনও শাখা নেই?
হাটসচাই

1
অসাধারণ. তৈরি করা যেতে পারে constexprএবং এই মত অপ্রয়োজনীয় টাইপ কাস্ট এড়াতে: template<typename T, typename U> constexpr auto fdiv( T t, U u ) -> decltype(t/(u+!u)) { return t/(u+!u); } আর যদি আপনি চান তাহলে 255,(lhs)/(rhs+!rhs) & -!rhs
Yakk - আদম Nevraumont

1
@leemes কিন্তু আমি কিছু বলতে চাইছেন |না &। ওফস - ( (lhs)/(rhs+!rhs) ) | -!rhsআপনার মানটি 0xFFFFFFFযদি rhsহয় 0এবং lhs/rhsযদি হয় সেট করে rhs!=0
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট


1
দুর্দান্ত উত্তর! আমি সাধারণত এই ধরণের জিনিসগুলির জন্য অ্যাসেম্বলির অবলম্বন করি তবে এটি বজায় রাখা সর্বদা ভয়ঙ্কর (কম পোর্টেবলের কথা উল্লেখ না করা)))
লিও

20

উইন্ডোতে জিসিসি ৪.7.২ ব্যবহার করে এখানে কয়েকটি কংক্রিট নম্বর রয়েছে:

#include <stdio.h>
#include <stdlib.h>

int main()
{
  unsigned int result = 0;
  for (int n = -500000000; n != 500000000; n++)
  {
    int d = -1;
    for (int i = 0; i != ITERATIONS; i++)
      d &= rand();

#if CHECK == 0
    if (d == 0) result++;
#elif CHECK == 1
    result += n / d;
#elif CHECK == 2
    result += n / (d + !d);
#elif CHECK == 3
    result += d == 0 ? 0 : n / d;
#elif CHECK == 4
    result += d == 0 ? 1 : n / d;
#elif CHECK == 5
    if (d != 0) result += n / d;
#endif
  }
  printf("%u\n", result);
}

নোট করুন যে আমি ইচ্ছাকৃতভাবে কল করছি না srand(), যাতে rand()সর্বদা একই ফলাফল ফিরে আসে। এছাড়াও নোট করুন যে -DCHECK=0নিছক শূন্যগুলি গণনা করা হয়, যাতে এটি স্পষ্ট হয় যে কতবার উপস্থিত হয়েছিল।

এখন, এটি বিভিন্ন উপায়ে সংকলন এবং সময় নির্ধারণ:

$ for it in 0 1 2 3 4 5; do for ch in 0 1 2 3 4 5; do gcc test.cc -o test -O -DITERATIONS=$it -DCHECK=$ch && { time=`time ./test`; echo "Iterations $it, check $ch: exit status $?, output $time"; }; done; done

টেবিলের সংক্ষিপ্তসারযোগ্য আউটপুট দেখায়:

Iterations  | 0        | 1        | 2        | 3         | 4         | 5
-------------+-------------------------------------------------------------------
Zeroes       | 0        | 1        | 133173   | 1593376   | 135245875 | 373728555
Check 1      | 0m0.612s | -        | -        | -         | -         | -
Check 2      | 0m0.612s | 0m6.527s | 0m9.718s | 0m13.464s | 0m18.422s | 0m22.871s
Check 3      | 0m0.616s | 0m5.601s | 0m8.954s | 0m13.211s | 0m19.579s | 0m25.389s
Check 4      | 0m0.611s | 0m5.570s | 0m9.030s | 0m13.544s | 0m19.393s | 0m25.081s
Check 5      | 0m0.612s | 0m5.627s | 0m9.322s | 0m14.218s | 0m19.576s | 0m25.443s

শূন্যগুলি বিরল হলে -DCHECK=2সংস্করণটি খারাপভাবে সম্পাদন করে। শূন্যগুলি আরও প্রদর্শিত হতে শুরু করার সাথে সাথে কেসটি আরও -DCHECK=2ভালভাবে সম্পাদন শুরু করে। অন্যান্য বিকল্পগুলির মধ্যে সত্যিকার অর্থে খুব বেশি পার্থক্য নেই।

জন্য -O3, যদিও, এটি একটি ভিন্ন গল্প হল:

Iterations  | 0        | 1        | 2        | 3         | 4         | 5
-------------+-------------------------------------------------------------------
Zeroes       | 0        | 1        | 133173   | 1593376   | 135245875 | 373728555
Check 1      | 0m0.646s | -        | -        | -         | -         | -
Check 2      | 0m0.654s | 0m5.670s | 0m9.905s | 0m14.238s | 0m17.520s | 0m22.101s
Check 3      | 0m0.647s | 0m5.611s | 0m9.085s | 0m13.626s | 0m18.679s | 0m25.513s
Check 4      | 0m0.649s | 0m5.381s | 0m9.117s | 0m13.692s | 0m18.878s | 0m25.354s
Check 5      | 0m0.649s | 0m6.178s | 0m9.032s | 0m13.783s | 0m18.593s | 0m25.377s

সেখানে, চেক 2 এর অন্যান্য চেকগুলির তুলনায় কোনও অসুবিধা নেই এবং শূণ্যগুলি আরও সাধারণ হয়ে যাওয়ার ফলে এটি সুবিধাগুলি রাখে keep

আপনার সংকলক এবং আপনার প্রতিনিধি নমুনা ডেটার সাথে কী ঘটে তা দেখতে আপনার সত্যই পরিমাপ করা উচিত।


4
এন্ট্রিগুলির 50% d=0প্রায় সর্বদা তৈরি করার পরিবর্তে এলোমেলোভাবে করুন d!=0এবং আপনি আরও শাখার পূর্বাভাস ব্যর্থতা দেখতে পাবেন। যদি একটি শাখা প্রায় সর্বদা অনুসরণ করা হয়, বা একটি শাখার নীচে বা
অন্যটির

@ ইয়াক্ক dপুনরাবৃত্তিটি অভ্যন্তরীণ লুপ, সুতরাং d == 0কেসগুলি সমানভাবে বিতরণ করা হয়। এবং 50% ক্ষেত্রে d == 0বাস্তববাদী করা হয়?

2
করছে 0.002%মামলার d==0বাস্তবসম্মত? এগুলি আপনার বিতরণে প্রতি 65000 পুনরুক্তি জুড়ে বিতরণ করা হয় d==0। যদিও 50%শক্তি প্রায়ই ঘটতে না, 10%বা 1%সহজে ঘটতে পারে, অথবা এমনকি 90%বা 99%। প্রদর্শিত পরীক্ষায় কেবল সত্যই পরীক্ষা করা হয় "যদি আপনি মূলত কখনও না হন, কখনও কোনও শাখায় নামেন না, শাখার পূর্বাভাস শাখাটিকে কী অর্থহীন করে তোলে?", যার উত্তর "হ্যাঁ, তবে এটি আকর্ষণীয় নয়"।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

1
না, কারণ শব্দগুলির কারণে পার্থক্যগুলি কার্যকরভাবে অদৃশ্য হয়ে থাকবে।
জো

3
জিরোগুলির বিতরণ প্রশ্নকর্তার পরিস্থিতিতে পাওয়া বিতরণের সাথে সম্পর্কিত নয়। 0 টি আলফা এবং অন্যান্য মিশ্রিত চিত্রগুলিতে গর্ত বা অনিয়মিত আকার থাকে তবে (সাধারণত) এটি গোলমাল নয়। আপনি ডেটা সম্পর্কে কিছুই জানেন না ধরে নেওয়া (এবং এটি শব্দ হিসাবে বিবেচনা করুন) একটি ভুল is এটি আসল চিত্র সহ একটি আসল ওয়ার্ল্ড অ্যাপ্লিকেশন যার মধ্যে 0 টি আলফা থাকতে পারে। এবং যেহেতু পিক্সেলের এক সারিতে সমস্ত a = 0 বা সমস্ত a> 0 হওয়ার সম্ভাবনা রয়েছে তাই শাখার পূর্বাভাসের সুবিধা গ্রহণ করা খুব দ্রুততম হতে পারে, বিশেষত যখন a = 0 অনেক ঘটে এবং (ধীর) বিভাজন (15+ চক্র) !) এড়ানো হয়।
ডিডিএস

13

প্ল্যাটফর্মটি না জেনে সঠিকভাবে সবচেয়ে কার্যকর পদ্ধতিটি জানার উপায় নেই, তবে, জেনেরিক সিস্টেমে এটি সর্বোত্তমের কাছাকাছি যেতে পারে (ইন্টেল এসেম্বলারের সিনট্যাক্স ব্যবহার করে):

(ধরুন বিভাজকটি রয়েছে ecxএবং লভ্যাংশ রয়েছে eax)

mov ebx, ecx
neg ebx
sbb ebx, ebx
add ecx, ebx
div eax, ecx

চারটি আন-ব্রাঞ্চযুক্ত, একক-চক্র নির্দেশাবলী এবং বিভাজন। ভাগফলটি থাকবে eaxএবং বাকীটি edxশেষে থাকবে। (আপনি কেন কোনও পুরুষের কাজ করার জন্য কোনও সংকলক প্রেরণ করতে চান না তা এই জাতীয় শো)।



1
এটি বিভাগটি করে না এটি কেবল বিভাজনকে দূষিত করে যাতে শূন্য দ্বারা ভাগ করা অসম্ভব
টাইলার ডারডেন ২

@ জেনস টিমারম্যান দুঃখিত, আমি বিভক্তির বিবৃতি যুক্ত করার আগে লিখেছিলাম। আমি লেখাটি আপডেট করেছি।
টাইলার ডারডেন

1

এই লিঙ্ক অনুসারে , আপনি কেবল এটি দিয়ে সিএফএফপিই সিগন্যালটি ব্লক করতে পারেন sigaction()(আমি নিজে এটি চেষ্টা করি নি, তবে আমি বিশ্বাস করি এটি কাজ করা উচিত)।

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

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.