আমার কোডটি দ্রুততর করে দেখুন?


1503

চেষ্টা-প্রভাবের পরীক্ষার জন্য আমি কিছু কোড লিখেছি, তবে কিছু বিস্ময়কর ফলাফল দেখে।

static void Main(string[] args)
{
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

    long start = 0, stop = 0, elapsed = 0;
    double avg = 0.0;

    long temp = Fibo(1);

    for (int i = 1; i < 100000000; i++)
    {
        start = Stopwatch.GetTimestamp();
        temp = Fibo(100);
        stop = Stopwatch.GetTimestamp();

        elapsed = stop - start;
        avg = avg + ((double)elapsed - avg) / i;
    }

    Console.WriteLine("Elapsed: " + avg);
    Console.ReadKey();
}

static long Fibo(int n)
{
    long n1 = 0, n2 = 1, fibo = 0;
    n++;

    for (int i = 1; i < n; i++)
    {
        n1 = n2;
        n2 = fibo;
        fibo = n1 + n2;
    }

    return fibo;
}

আমার কম্পিউটারে এটি নিয়মিত 0.96 এর কাছাকাছি একটি মান ছাপে ..

আমি যখন ফিবোর () এর অভ্যন্তরীণ জন্য লুপটি এইভাবে চেষ্টা করি:

static long Fibo(int n)
{
    long n1 = 0, n2 = 1, fibo = 0;
    n++;

    try
    {
        for (int i = 1; i < n; i++)
        {
            n1 = n2;
            n2 = fibo;
            fibo = n1 + n2;
        }
    }
    catch {}

    return fibo;
}

এখন এটি নিয়মিত 0.69 মুদ্রণ করে ... - এটি আসলে দ্রুত চলে! কিন্তু কেন?

দ্রষ্টব্য: আমি এটি প্রকাশের কনফিগারেশনটি ব্যবহার করে সংকলন করেছি এবং সরাসরি EXE ফাইল চালিয়েছি (ভিজ্যুয়াল স্টুডিওর বাইরে)।

সম্পাদনা: জন স্কিটির দুর্দান্ত বিশ্লেষণ দেখায় যে ট্রাই -ক্যাচ একরকমভাবে x86 সিএলআরকে এই নির্দিষ্ট ক্ষেত্রে আরও অনুকূল উপায়ে সিপিইউ রেজিস্টার ব্যবহার করতে বাধ্য করেছে (এবং আমি মনে করি কেন আমরা এখনও বুঝতে পারি নি)। আমি জোনকে নিশ্চিত করেছিলাম যে এক্স 64 সিএলআর এর পার্থক্য নেই এবং এটি x86 সিএলআর এর চেয়ে দ্রুত ছিল। আমি intটাইপের পরিবর্তে ফিবো পদ্ধতির অভ্যন্তরে প্রকারগুলি ব্যবহার করে পরীক্ষা করেছি longএবং তারপরে x86 সিএলআর x64 সিএলআর হিসাবে তত দ্রুত ছিল।


আপডেট: দেখে মনে হচ্ছে এই সমস্যাটি রোজলিন ঠিক করেছেন। একই মেশিন, একই সিএলআর সংস্করণ - ভিএস 2013 এর সাথে সংকলিত হওয়ার সময় সমস্যাটি উপরের মতো থেকে যায়, তবে ভিএস 2015 এর সাথে সংকলিত হলে সমস্যাটি চলে যায়।


111
@ লয়েড তার প্রশ্নের উত্তর পাওয়ার চেষ্টা করেছেন "এটি আসলে দ্রুত চলে! তবে কেন?"
Andreas Niedermair

137
সুতরাং, এখন "গেলা ব্যতিক্রমগুলি" একটি খারাপ অনুশীলন থেকে ভাল পারফরম্যান্স অপ্টিমাইজেশনের দিকে চলে গেছে: পি
লুসিয়ানো

2
এটি কি চেক করা বা পরীক্ষিত গাণিতিক প্রসঙ্গে?
র্যান্ডম 832

7
@ তারাস.রোশকো: যদিও আমি এরিককে বিচ্ছিন্নতা করতে চাই না, এটি আসলে কোনও সি # প্রশ্ন নয় - এটি একটি জেআইটি সংকলক প্রশ্ন। চূড়ান্ত অসুবিধা কাজ করছে কেন এক্স 86 জে আই টি JIT চেষ্টা / ধরা ছাড়া অনেক রেজিস্টার হিসাবে ব্যবহার করে না এটা আছে সঙ্গে ব্যবহার করে দেখুন / ধরা ব্লক।
জন স্কিটি

63
মিষ্টি, সুতরাং আমরা যদি এই চেষ্টা করে বাঁচি তবে আমরা আরও দ্রুত যেতে পারি?
চাক পিংকার্ট

উত্তর:


1053

রোজলিন ইঞ্জিনিয়ারদের একজন যিনি স্ট্যাক ব্যবহারের অপ্টিমাইজেশন বোঝার ক্ষেত্রে বিশেষজ্ঞ হন তাদের এক নজরে দেখেছিলেন এবং আমাকে রিপোর্ট করেছেন যে সি # সংকলক যেভাবে স্থানীয় ভেরিয়েবল স্টোর তৈরি করে এবং জেআইটি সংকলক যেভাবে নিবন্ধভুক্ত করে তার মধ্যে মিথস্ক্রিয়ায় সমস্যা রয়েছে বলে মনে হচ্ছে সংশ্লিষ্ট x86 কোডের সময়সূচী। ফলাফল স্থানীয় লোড এবং স্টোর মধ্যে suboptimal কোড উত্পাদন।

কিছু কারণে আমাদের সবার কাছে অস্পষ্ট, সমস্যাযুক্ত কোড জেনারেশনের পথটি এড়ানো যায় যখন জেআইটিটার জানে যে ব্লকটি চেষ্টা-সুরক্ষিত অঞ্চলে রয়েছে।

এটি বেশ অদ্ভুত। আমরা জিটটার টিমের সাথে ফলোআপ করব এবং দেখব যে আমরা কোনও বাগ প্রবেশ করতে পারি যাতে তারা এটি ঠিক করতে পারে।

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

এটি আমাদের নজরে আনার জন্য ধন্যবাদ এবং বিজোড় আচরণের জন্য ক্ষমা চাই।


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

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

24
এই ইস্যুতে কখনও কোন আন্দোলন হয়েছিল?
রবার্ট হার্ভে

10
দেখে মনে হচ্ছে রোজলিন ঠিক করে দিয়েছে।
এরেন এরসনেমেজ

56
আপনি এটিকে "জেটার বাগ" বলার সুযোগটি হাতছাড়া করেছেন।
mbomb007

734

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

var stopwatch = Stopwatch.StartNew();
for (int i = 1; i < 100000000; i++)
{
    Fibo(100);
}
stopwatch.Stop();
Console.WriteLine("Elapsed time: {0}", stopwatch.Elapsed);

এইভাবে আপনি ক্ষুদ্র সময়, ভাসমান পয়েন্ট গণিত এবং জমে থাকা ত্রুটির দয়ায় নেই।

এই পরিবর্তনটি করে, দেখুন "নন-ক্যাচ" সংস্করণটি "ক্যাচ" সংস্করণটির থেকে এখনও ধীর whether

সম্পাদনা: ঠিক আছে, আমি নিজে চেষ্টা করেছি - এবং আমি একই ফলাফল দেখছি। খুব অদ্ভুত. চেষ্টা / ক্যাচটি কিছু খারাপ ইনলাইনিং অক্ষম করছে কিনা তা নিয়ে আমি অবাক হয়েছি, তবে এর [MethodImpl(MethodImplOptions.NoInlining)]পরিবর্তে ব্যবহার করা কোনও উপকারে আসেনি ...

মূলত আপনাকে কর্ডবিজির আওতায় অনুকূলিত জেআইটিড কোডটি দেখতে হবে, আমি সন্দেহ করি ...

সম্পাদনা: আরও কয়েকটি বিটের তথ্য:

  • কেবল n++;লাইনের চারপাশে চেষ্টা / ধরার ফলে কর্মক্ষমতা উন্নত হয় তবে পুরো ব্লকের চারপাশে রাখার মতো নয়
  • আপনি যদি একটি নির্দিষ্ট ব্যতিক্রম ( ArgumentExceptionআমার পরীক্ষাগুলিতে) ধরে থাকেন তবে এটি এখনও দ্রুত
  • আপনি যদি ক্যাচ ব্লকে ব্যতিক্রমটি মুদ্রণ করেন তবে এটি এখনও দ্রুত
  • আপনি যদি ক্যাচ ব্লকে ব্যতিক্রমটি পুনর্বিবেচনা করেন তবে এটি আবার ধীর
  • আপনি যদি ক্যাচ ব্লকের পরিবর্তে শেষ অবধি ব্যবহার করেন তবে এটি আবার ধীর slow
  • আপনি যদি শেষ অবধি পাশাপাশি একটি ক্যাচ ব্লক ব্যবহার করেন তবে এটি দ্রুত

অদ্ভুত ...

সম্পাদনা: ঠিক আছে, আমাদের বিচ্ছিন্ন করা হয়েছে ...

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

দ্রুত সংস্করণের জন্য সংস্থানবিহীন কোড:

 [0000] push        ebp
 [0001] mov         ebp,esp
 [0003] push        edi
 [0004] push        esi
 [0005] push        ebx
 [0006] sub         esp,1Ch
 [0009] xor         eax,eax
 [000b] mov         dword ptr [ebp-20h],eax
 [000e] mov         dword ptr [ebp-1Ch],eax
 [0011] mov         dword ptr [ebp-18h],eax
 [0014] mov         dword ptr [ebp-14h],eax
 [0017] xor         eax,eax
 [0019] mov         dword ptr [ebp-18h],eax
*[001c] mov         esi,1
 [0021] xor         edi,edi
 [0023] mov         dword ptr [ebp-28h],1
 [002a] mov         dword ptr [ebp-24h],0
 [0031] inc         ecx
 [0032] mov         ebx,2
 [0037] cmp         ecx,2
 [003a] jle         00000024
 [003c] mov         eax,esi
 [003e] mov         edx,edi
 [0040] mov         esi,dword ptr [ebp-28h]
 [0043] mov         edi,dword ptr [ebp-24h]
 [0046] add         eax,dword ptr [ebp-28h]
 [0049] adc         edx,dword ptr [ebp-24h]
 [004c] mov         dword ptr [ebp-28h],eax
 [004f] mov         dword ptr [ebp-24h],edx
 [0052] inc         ebx
 [0053] cmp         ebx,ecx
 [0055] jl          FFFFFFE7
 [0057] jmp         00000007
 [0059] call        64571ACB
 [005e] mov         eax,dword ptr [ebp-28h]
 [0061] mov         edx,dword ptr [ebp-24h]
 [0064] lea         esp,[ebp-0Ch]
 [0067] pop         ebx
 [0068] pop         esi
 [0069] pop         edi
 [006a] pop         ebp
 [006b] ret

ধীর সংস্করণের জন্য সংস্থানবিহীন কোড:

 [0000] push        ebp
 [0001] mov         ebp,esp
 [0003] push        esi
 [0004] sub         esp,18h
*[0007] mov         dword ptr [ebp-14h],1
 [000e] mov         dword ptr [ebp-10h],0
 [0015] mov         dword ptr [ebp-1Ch],1
 [001c] mov         dword ptr [ebp-18h],0
 [0023] inc         ecx
 [0024] mov         esi,2
 [0029] cmp         ecx,2
 [002c] jle         00000031
 [002e] mov         eax,dword ptr [ebp-14h]
 [0031] mov         edx,dword ptr [ebp-10h]
 [0034] mov         dword ptr [ebp-0Ch],eax
 [0037] mov         dword ptr [ebp-8],edx
 [003a] mov         eax,dword ptr [ebp-1Ch]
 [003d] mov         edx,dword ptr [ebp-18h]
 [0040] mov         dword ptr [ebp-14h],eax
 [0043] mov         dword ptr [ebp-10h],edx
 [0046] mov         eax,dword ptr [ebp-0Ch]
 [0049] mov         edx,dword ptr [ebp-8]
 [004c] add         eax,dword ptr [ebp-1Ch]
 [004f] adc         edx,dword ptr [ebp-18h]
 [0052] mov         dword ptr [ebp-1Ch],eax
 [0055] mov         dword ptr [ebp-18h],edx
 [0058] inc         esi
 [0059] cmp         esi,ecx
 [005b] jl          FFFFFFD3
 [005d] mov         eax,dword ptr [ebp-1Ch]
 [0060] mov         edx,dword ptr [ebp-18h]
 [0063] lea         esp,[ebp-4]
 [0066] pop         esi
 [0067] pop         ebp
 [0068] ret

প্রতিটি ক্ষেত্রেই *ডিবাগারটি একটি সাধারণ "স্টেপ-ইন" এ প্রবেশ করে।

সম্পাদনা: ঠিক আছে, আমি এখন কোডটি দেখেছি এবং আমি মনে করি প্রতিটি সংস্করণ কীভাবে কাজ করে তা আমি দেখতে পাচ্ছি ... এবং আমি বিশ্বাস করি যে ধীর সংস্করণটি ধীরতর কারণ এটিতে কম রেজিস্টার এবং আরও স্ট্যাক স্পেস ব্যবহার করা হয়েছে। এর ছোট মানগুলির জন্য nসম্ভবত দ্রুততর - তবে যখন লুপটি বেশিরভাগ সময় নেয়, এটি ধীর হয়।

সম্ভবত চেষ্টা / ধরা ব্লক বাহিনী আরো রেজিস্টার সংরক্ষিত করা এবং পুনরুদ্ধার, তাই জে আই টি JIT লুপ জন্য ঐ পাশাপাশি ব্যবহার ... যা সামগ্রিক পারফরম্যান্সের উন্নতি ঘটবে। "সাধারণ" কোডে যত বেশি রেজিস্টার ব্যবহার না করা জেআইটির পক্ষে যুক্তিসঙ্গত সিদ্ধান্ত ছিল কিনা তা পরিষ্কার নয় ।

সম্পাদনা: আমার x64 মেশিনে এটি চেষ্টা করে দেখুন tried এই কোডের x86 সিএলআরের তুলনায় x64 সিএলআর অনেক দ্রুত (প্রায় 3-4 গুণ দ্রুত) এবং x64 এর অধীনে চেষ্টা / ক্যাচ ব্লকটি লক্ষণীয় পার্থক্য করে না।


4
@ গর্ডনসিম্পসন তবে সেই ক্ষেত্রে যেখানে কেবলমাত্র একটি নির্দিষ্ট ব্যতিক্রম ধরা পড়ে তবে অন্য সমস্ত ব্যতিক্রম ধরা পড়বে না, সুতরাং চেষ্টা না করার জন্য আপনার হাইপোথিসিসে যে ওভারহেড জড়িত তা এখনও প্রয়োজন হবে still
জন হান্না

45
এটি নিবন্ধের বরাদ্দের মধ্যে একটি পার্থক্যের মতো দেখায়। দ্রুত সংস্করণটি esi,ediস্ট্যাকের পরিবর্তে দীর্ঘতর একের জন্য ব্যবহার করতে পরিচালিত করে । এটি ebxকাউন্টার হিসাবে ব্যবহার করে, যেখানে ধীর সংস্করণ ব্যবহার করে esi
জেফ্রি স্যাক্স

13
@ জেফ্রেস্যাক্স: কেবল কোন রেজিস্টারগুলি ব্যবহৃত হয় তা নয়, কয়টি ব্যবহার করা হয়। ধীর সংস্করণটি কম রেজিস্টারগুলিকে স্পর্শ করে আরও স্ট্যাক স্পেস ব্যবহার করে। আমি জানিনা কেন ...
জন স্কিটে

2
সিএলআর ব্যতিক্রম ফ্রেমগুলি কীভাবে রেজিস্টার এবং স্ট্যাকের ক্ষেত্রে মোকাবেলা করা হয়? একটি সেট আপ কি কোনওভাবে ব্যবহারের জন্য একটি রেজিস্টার মুক্ত করতে পারে?
র্যান্ডম 832

4
আইআইআরসি x64 এর এক্স x86 এর চেয়ে বেশি রেজিস্টার উপলব্ধ। আপনি যে স্পিডআপটি দেখেছেন তা x86 এর অধীনে অতিরিক্ত রেজিস্টার ব্যবহারের চেষ্টা / ধরার সাথে সামঞ্জস্যপূর্ণ।
ড্যান ইজ ফিডলিং ফায়ারলাইট

116

জনের বিচ্ছিন্নতাগুলি দেখায় যে দুটি সংস্করণের মধ্যে পার্থক্য হ'ল দ্রুত সংস্করণটি esi,ediস্থানীয় ভেরিয়েবলগুলির মধ্যে যে কোনও একটি ধীরে ধীরে সংস্করণটি সংরক্ষণ করে না তার জন্য একজোড়া রেজিস্টার ( ) ব্যবহার করে।

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

শেষ পর্যন্ত, কোন কোডটি দ্রুততম চলতে শুরু করবে তা বলা খুব শক্ত। রেজিস্টার বরাদ্দের মতো কিছু এবং এটি প্রভাবিত করার কারণগুলি এমন নিম্ন-স্তরের বাস্তবায়ন বিশদ যা আমি দেখতে পাই না যে কোনও নির্দিষ্ট কৌশল কীভাবে নির্ভরযোগ্যভাবে দ্রুত কোড তৈরি করতে পারে।

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

interface IIndexed { int this[int index] { get; set; } }
struct StructArray : IIndexed { 
    public int[] Array;
    public int this[int index] {
        get { return Array[index]; }
        set { Array[index] = value; }
    }
}

static int Generic<T>(int length, T a, T b) where T : IIndexed {
    int sum = 0;
    for (int i = 0; i < length; i++)
        sum += a[i] * b[i];
    return sum;
}
static int Specialized(int length, StructArray a, StructArray b) {
    int sum = 0;
    for (int i = 0; i < length; i++)
        sum += a[i] * b[i];
    return sum;
}

একটি অন্যটির জেনেরিক সংস্করণ। জেনেরিক ধরণের সাথে প্রতিস্থাপন করা StructArrayপদ্ধতিগুলি অভিন্ন করে তুলবে। কারণ StructArrayএকটি মান ধরণের, এটি জেনেরিক পদ্ধতির নিজস্ব সংকলিত সংস্করণ পায়। তবুও প্রকৃত চলমান সময়টি বিশেষায়িত পদ্ধতির চেয়ে উল্লেখযোগ্যভাবে দীর্ঘ, তবে কেবল x86 এর জন্য। এক্স 64৪ এর জন্য সময়গুলি বেশ অনেকটা অভিন্ন। অন্যান্য ক্ষেত্রে, আমি x64 এর জন্যও পার্থক্য লক্ষ্য করেছি।


6
যা বলা হচ্ছে তার সাথে ... আপনি কি চেষ্টা / ক্যাচ ব্যবহার না করে বিভিন্ন নিবন্ধের বরাদ্দ পছন্দকে বাধ্য করতে পারেন? হয় এই হাইপোথিসিসের পরীক্ষা হিসাবে বা গতির জন্য টুইট করার সাধারণ প্রচেষ্টা হিসাবে?
WernerCD

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

4
@ ওয়ার্ননার সিডি আমি এই সত্যটি বলতে চাই যে সি এবং সি ++ এর একটি কীওয়ার্ড রয়েছে যা অনেক আধুনিক সংকলক দ্বারা (এ) উপেক্ষা করা হয় এবং (বি) সি # তে না রাখার সিদ্ধান্ত নেওয়া হয়েছিল, তা বোঝায় যে এটি আমরা এমন কিছু নয় ' আরও কোনও সরাসরি উপায়ে দেখতে পাবেন।
জন হান্না

2
@ ওয়ার্ননারসিডি - কেবল আপনি
নিজেরাই সমাবেশটি লিখলে

72

এটি দেখে মনে হচ্ছে ইনলাইন করার ঘটনাটি খারাপ হয়ে গেছে। একটি x86 কোর-এ, জিটারটিতে স্থানীয় ভেরিয়েবলগুলির সাধারণ উদ্দেশ্যে সঞ্চয় করার জন্য ebx, edx, esi এবং edi রেজিস্টার থাকে। Ecx রেজিস্টার একটি স্ট্যাটিক পদ্ধতি উপলব্ধ হলে, এটা সঞ্চয় করতে নেই এই । ইক্স রেজিস্টার প্রায়শই গণনার প্রয়োজন হয়। তবে এগুলি 32-বিট রেজিস্টার, দীর্ঘ ধরণের ভেরিয়েবলের জন্য এটি অবশ্যই একজোড়া রেজিস্টার ব্যবহার করতে পারে। যেগুলি edx: গণনার জন্য eax এবং সঞ্চয় করার জন্য edi: ebx।

ধীর সংস্করণটির জন্য বিচ্ছিন্নতার মধ্যে কোনটি এডিআই বা ইবিএক্স ব্যবহার করা হয় না।

যখন জিটার স্থানীয় ভেরিয়েবলগুলি সংরক্ষণ করার জন্য পর্যাপ্ত নিবন্ধগুলি খুঁজে না পায় তখন স্ট্যাক ফ্রেম থেকে এগুলি লোড এবং সঞ্চয় করার জন্য কোড তৈরি করতে হবে। এটি কোডকে ধীর করে দেয়, এটি "রেজিস্টার নামকরণ" নামে একটি প্রসেসরের অপ্টিমাইজেশনকে বাধা দেয়, একটি অভ্যন্তরীণ প্রসেসর কোর অপ্টিমাইজেশন ট্রিক যা কোনও নিবন্ধের একাধিক অনুলিপি ব্যবহার করে এবং সুপার-স্কেলারের কার্যকরকরণের অনুমতি দেয়। যা একই রেজিস্টার ব্যবহার করে এমনকি একযোগে চালনার জন্য বেশ কয়েকটি নির্দেশকে অনুমতি দেয়। X86 কোরগুলিতে পর্যাপ্ত রেজিস্টার না পাওয়া একটি সাধারণ সমস্যা, এটি x64 এ সম্বোধন করা হয়েছে যার 8 টি অতিরিক্ত রেজিস্টার রয়েছে (r15 এর মাধ্যমে r9)।

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

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

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

সুতরাং আপনি দ্রুত সংস্করণটি পান কারণ চিকিত্সাটি জানেন যে পদ্ধতিটির শরীরে ট্রাই / ক্যাচ স্টেটমেন্ট রয়েছে। এটি জানে যে এটি কখনই ইনলাইন করা যায় না তাই দীর্ঘ পরিবর্তনশীলের জন্য সহজেই সঞ্চয় করার জন্য এডি: ইবিএক্স ব্যবহার করে। আপনি ধীর সংস্করণটি পেয়েছেন কারণ চিকিত্সাটি জানেন না যে ইনলাইনিং কাজ করবে না। এটি কেবল মেথড বডি জন্য কোড উত্পন্ন করার পরে খুঁজে পেয়েছিল ।

ত্রুটিটি হ'ল এটি পিছনে যায় না এবং পদ্ধতির জন্য কোডটি পুনরায় তৈরি করে না। এটি বোধগম্য, সময়সীমাবদ্ধতাগুলির মধ্যে এটি পরিচালনা করতে হবে।

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


21

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

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