সাধারণ বেঞ্চমার্কে অদ্ভুত অভিনয় বৃদ্ধি


97

গতকাল আমি "। নেট স্ট্রাক্ট পারফরম্যান্স" শিরোনামে ক্রিস্টোফ নাহরের একটি নিবন্ধ পেয়েছি যা একটি পদ্ধতির জন্য দুটি পয়েন্ট স্ট্রাক্ট ( doubleটিপলস) যুক্ত করার জন্য কয়েকটি ভাষা (সি ++, সি #, জাভা, জাভাস্ক্রিপ্ট) বেঞ্চমার্ক করেছে ।

দেখা গেছে, সি ++ সংস্করণটি কার্যকর করতে প্রায় 1000 মিমি লাগবে (1e9 পুনরাবৃত্তি), যখন সি # একই মেশিনে 000 3000ms এর অধীনে পেতে পারে না (এবং x64 এর চেয়েও খারাপ সম্পাদন করে)।

এটি নিজে পরীক্ষা করার জন্য, আমি সি # কোডটি নিয়েছি (এবং কেবলমাত্র সেই পদ্ধতিটি কল করার জন্য সামান্য সরলীকৃত যেখানে প্যারামিটারগুলি মান দ্বারা পাস হয়), এবং এটি i7-3610QM মেশিনে (একক কোরের জন্য 3.1 গিগাহার্টজ বুস্ট), 8 জিবি র‌্যাম, উইন 8 নিয়ে চলেছি। 1, .NET 4.5.2 ব্যবহার করে, 32-বিট বিল্ড রিলিজ করুন (আমার ওএস 64-বিট হওয়ায় x86 ওউ 64)। এটি সরলিকৃত সংস্করণ:

public static class CSharpTest
{
    private const int ITERATIONS = 1000000000;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static Point AddByVal(Point a, Point b)
    {
        return new Point(a.X + b.Y, a.Y + b.X);
    }

    public static void Main()
    {
        Point a = new Point(1, 1), b = new Point(1, 1);

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < ITERATIONS; i++)
            a = AddByVal(a, b);
        sw.Stop();

        Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms", 
            a.X, a.Y, sw.ElapsedMilliseconds);
    }
}

Pointসহজভাবে সংজ্ঞায়িত সহ :

public struct Point 
{
    private readonly double _x, _y;

    public Point(double x, double y) { _x = x; _y = y; }

    public double X { get { return _x; } }

    public double Y { get { return _y; } }
}

এটি চালানো নিবন্ধের মতো ফলাফল তৈরি করে:

Result: x=1000000001 y=1000000001, Time elapsed: 3159 ms

প্রথম অদ্ভুত পর্যবেক্ষণ

যেহেতু পদ্ধতিটি ইনলাইন করা উচিত, তাই আমি ভাবলাম যে কোডটি যদি পুরোপুরি স্ট্রাক্টগুলি সরিয়ে ফেলা হয় এবং পুরো জিনিসটি একসাথে linedোকানো হয় তবে কোডটি কীভাবে সম্পাদন করবে:

public static class CSharpTest
{
    private const int ITERATIONS = 1000000000;

    public static void Main()
    {
        // not using structs at all here
        double ax = 1, ay = 1, bx = 1, by = 1;

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < ITERATIONS; i++)
        {
            ax = ax + by;
            ay = ay + bx;
        }
        sw.Stop();

        Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms", 
            ax, ay, sw.ElapsedMilliseconds);
    }
}

এবং কার্যত একই ফলাফল পেয়েছে (বেশ কয়েকটি পুনরায় প্রচেষ্টার পরে আসলে 1% ধীর), যার অর্থ জেআইটি-টের মনে হয় যে সমস্ত ফাংশন কলকে অনুকূল করে একটি ভাল কাজ করছে:

Result: x=1000000001 y=1000000001, Time elapsed: 3200 ms

এর অর্থ হ'ল বেঞ্চমার্কটি কোনও structকার্যকারিতা পরিমাপ করে বলে মনে হচ্ছে না এবং এটি কেবলমাত্র মৌলিক doubleগাণিতিককে পরিমাপ করে বলে মনে হচ্ছে (সমস্ত কিছু সরিয়ে নেওয়ার পরে)।

অদ্ভুত জিনিস

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

public static void Main()
{
    var outerSw = Stopwatch.StartNew();     // <-- added

    {
        Point a = new Point(1, 1), b = new Point(1, 1);

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < ITERATIONS; i++)
            a = AddByVal(a, b);
        sw.Stop();

        Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms",
            a.X, a.Y, sw.ElapsedMilliseconds);
    }

    outerSw.Stop();                         // <-- added
}

Result: x=1000000001 y=1000000001, Time elapsed: 961 ms

যে হাস্যকর! এবং এটি Stopwatchআমার ভুল ফলাফল দেওয়ার মতো নয় কারণ আমি স্পষ্ট দেখতে পাচ্ছি যে এটি এক সেকেন্ড পরে শেষ হয়।

এখানে কি ঘটতে পারে কেউ আমাকে বলতে পারেন?

(হালনাগাদ)

এখানে একই প্রোগ্রামে দুটি পদ্ধতি রয়েছে যা দেখায় যে কারণটি জেটিটিং নয়:

public static class CSharpTest
{
    private const int ITERATIONS = 1000000000;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static Point AddByVal(Point a, Point b)
    {
        return new Point(a.X + b.Y, a.Y + b.X);
    }

    public static void Main()
    {
        Test1();
        Test2();

        Console.WriteLine();

        Test1();
        Test2();
    }

    private static void Test1()
    {
        Point a = new Point(1, 1), b = new Point(1, 1);

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < ITERATIONS; i++)
            a = AddByVal(a, b);
        sw.Stop();

        Console.WriteLine("Test1: x={0} y={1}, Time elapsed: {2} ms", 
            a.X, a.Y, sw.ElapsedMilliseconds);
    }

    private static void Test2()
    {
        var swOuter = Stopwatch.StartNew();

        Point a = new Point(1, 1), b = new Point(1, 1);

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < ITERATIONS; i++)
            a = AddByVal(a, b);
        sw.Stop();

        Console.WriteLine("Test2: x={0} y={1}, Time elapsed: {2} ms", 
            a.X, a.Y, sw.ElapsedMilliseconds);

        swOuter.Stop();
    }
}

আউটপুট:

Test1: x=1000000001 y=1000000001, Time elapsed: 3242 ms
Test2: x=1000000001 y=1000000001, Time elapsed: 974 ms

Test1: x=1000000001 y=1000000001, Time elapsed: 3251 ms
Test2: x=1000000001 y=1000000001, Time elapsed: 972 ms

এখানে একটি পেস্টবিন রয়েছে। আপনাকে নেট। এনটি 4.x এ 32-বিট রিলিজ হিসাবে চালানো দরকার (এটি নিশ্চিত করার জন্য কোডটিতে কয়েকটি চেক রয়েছে)।

(আপডেট 4)

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

বামদিকে টেস্ট 1, ডানদিকে টেস্ট 2

এটি দেখা যাচ্ছে যে ডাবল ফিল্ড প্রান্তিককরণের চেয়ে প্রথম ক্ষেত্রে মজাদার অভিনীত সংকলকের কারণে এই পার্থক্যটি হতে পারে?

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

// this is still fast?
private static void Test3()
{
    var magical_speed_booster_1 = "whatever";
    var magical_speed_booster_2 = "whatever";

    {
        Point a = new Point(1, 1), b = new Point(1, 1);

        var sw = Stopwatch.StartNew();
        for (int i = 0; i < ITERATIONS; i++)
            a = AddByVal(a, b);
        sw.Stop();

        Console.WriteLine("Test2: x={0} y={1}, Time elapsed: {2} ms",
            a.X, a.Y, sw.ElapsedMilliseconds);
    }

    GC.KeepAlive(magical_speed_booster_1);
    GC.KeepAlive(magical_speed_booster_2);
}

4
জেআইটি জিনিসটির পাশাপাশি এটি সংকলকটির অপ্টিমাইজেশনের উপরও নির্ভর করে, সর্বাধিক রিউজিৎ আরও বেশি অপ্টিমাইজেশন করে এবং এমনকি সীমাবদ্ধ সিমডি নির্দেশাবলী সমর্থনও প্রবর্তন করে।
ফেলিক্স কে।

4
জোন স্কিট স্ট্রোকগুলিতে কেবল পাঠযোগ্য ক্ষেত্রগুলির সাথে একটি পারফরম্যান্স সমস্যা পেয়েছে: মাইক্রো-অপ্টিমাইজেশন: কেবলমাত্র পাঠ্য ক্ষেত্রগুলির মধ্যে আশ্চর্যজনক অক্ষমতা । বেসরকারী ক্ষেত্রগুলি অ-পঠনযোগ্য করার চেষ্টা করুন।
dbc

4
@ ডিবিসি: আমি কেবল স্থানীয় doubleভেরিয়েবলগুলির সাথে একটি পরীক্ষা structকরেছি , কোন এস, তাই আমি স্ট্রাক্ট লেআউট / পদ্ধতি কলের অক্ষমতার বিষয়টি অস্বীকার করেছি।
গ্রু

4
কেবল 32-বিটে ঘটবে বলে মনে হচ্ছে, রিউজিআইটি সহ, আমি উভয় বার 1600 মিমি পেয়েছি।
লেপি

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

উত্তর:


10

আপডেট 4 সমস্যাটি ব্যাখ্যা করে: প্রথম ক্ষেত্রে, জেআইটি গণিত মানগুলি ( a, b) স্ট্যাকের উপরে রাখে ; দ্বিতীয় ক্ষেত্রে, জেআইটি এটি রেজিস্টারে রাখে।

আসলে, Test1কারণ ধীরে ধীরে কাজ করে Stopwatch। আমি উপর ভিত্তি করে নিম্নলিখিত ন্যূনতম বেঞ্চমার্ক লিখেছিলেন BenchmarkDotNet :

[BenchmarkTask(platform: BenchmarkPlatform.X86)]
public class Jit_RegistersVsStack
{
    private const int IterationCount = 100001;

    [Benchmark]
    [OperationsPerInvoke(IterationCount)]
    public string WithoutStopwatch()
    {
        double a = 1, b = 1;
        for (int i = 0; i < IterationCount; i++)
        {
            // fld1  
            // faddp       st(1),st
            a = a + b;
        }
        return string.Format("{0}", a);
    }

    [Benchmark]
    [OperationsPerInvoke(IterationCount)]
    public string WithStopwatch()
    {
        double a = 1, b = 1;
        var sw = new Stopwatch();
        for (int i = 0; i < IterationCount; i++)
        {
            // fld1  
            // fadd        qword ptr [ebp-14h]
            // fstp        qword ptr [ebp-14h]
            a = a + b;
        }
        return string.Format("{0}{1}", a, sw.ElapsedMilliseconds);
    }

    [Benchmark]
    [OperationsPerInvoke(IterationCount)]
    public string WithTwoStopwatches()
    {
        var outerSw = new Stopwatch();
        double a = 1, b = 1;
        var sw = new Stopwatch();
        for (int i = 0; i < IterationCount; i++)
        {
            // fld1  
            // faddp       st(1),st
            a = a + b;
        }
        return string.Format("{0}{1}", a, sw.ElapsedMilliseconds);
    }
}

আমার কম্পিউটারে ফলাফল:

BenchmarkDotNet=v0.7.7.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4702MQ CPU @ 2.20GHz, ProcessorCount=8
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit  [RyuJIT]
Type=Jit_RegistersVsStack  Mode=Throughput  Platform=X86  Jit=HostJit  .NET=HostFramework

             Method |   AvrTime |    StdDev |       op/s |
------------------- |---------- |---------- |----------- |
   WithoutStopwatch | 1.0333 ns | 0.0028 ns | 967,773.78 |
      WithStopwatch | 3.4453 ns | 0.0492 ns | 290,247.33 |
 WithTwoStopwatches | 1.0435 ns | 0.0341 ns | 958,302.81 |

যেভাবে আমরা দেখি:

  • WithoutStopwatchদ্রুত কাজ করে (কারণ নিবন্ধগুলি a = a + bব্যবহার করে)
  • WithStopwatchধীরে ধীরে কাজ করে (কারণ a = a + bস্ট্যাকটি ব্যবহার করে)
  • WithTwoStopwatchesদ্রুত আবার কাজ করে (কারণ নিবন্ধগুলি a = a + bব্যবহার করে)

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


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

75

আপনার প্রোগ্রামটির সর্বদা "দ্রুত" সংস্করণ পাওয়ার জন্য একটি খুব সহজ উপায় রয়েছে। প্রকল্প> বৈশিষ্ট্য> বিল্ড ট্যাবটি, "32-বিট পছন্দ করুন" বিকল্পটি অনিচ্ছুক করুন, প্ল্যাটফর্ম লক্ষ্য নির্বাচন যে কোনও এসিপিইউ রয়েছে তা নিশ্চিত করুন।

আপনি সত্যই 32-বিট পছন্দ করেন না, দুর্ভাগ্যক্রমে সি # প্রকল্পের জন্য সর্বদা ডিফল্টরূপে চালু থাকে। Orতিহাসিকভাবে, ভিজ্যুয়াল স্টুডিও টুলসেটটি 32-বিট প্রক্রিয়াগুলির সাথে অনেক বেশি ভাল কাজ করেছে, এটি একটি পুরানো সমস্যা যা মাইক্রোসফ্ট ছাড়ছে। এই বিকল্পটি সরিয়ে নেওয়ার সময়, বিশেষত ভিএস ২০১৫ শেষ ব্র্যান্ড-নিউ এক্স 64৪ জিটার এবং সম্পাদনা + চালিয়ে যাওয়ার সার্বজনীন সমর্থন সহ few৪-বিট কোডে শেষ কয়েকটি বাস্তব রাস্তা-ব্লকগুলিকে সম্বোধন করেছে।

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

doubleএবং longধরনের একটি 32 বিট প্রক্রিয়ায় কষ্ট-প্রস্তুতকারকদের হয়। এগুলি আকারে 64-বিট। এবং এভাবে 4 দ্বারা বিভ্রান্ত হয়ে উঠতে পারে, সিএলআর কেবল একটি 32-বিট প্রান্তিককরণের গ্যারান্টি দিতে পারে। -৪-বিট প্রক্রিয়াতে কোনও সমস্যা নয়, সমস্ত ভেরিয়েবলগুলি 8-এ সারিবদ্ধ হওয়ার গ্যারান্টিযুক্ত এছাড়াও এছাড়াও সি # ভাষা কেন তাদের পারমাণবিক হওয়ার প্রতিশ্রুতি দিতে পারে না তার অন্তর্নিহিত কারণ । এবং যখন তাদের 1000 টিরও বেশি উপাদান থাকে তখন ডাবলের অ্যারেগুলি বড় অবজেক্ট হিপগুলিতে বরাদ্দ করা হয়। এলওএইচ ৮ এর প্রান্তিককরণের গ্যারান্টি সরবরাহ করে এবং ব্যাখ্যা করে কেন একটি স্থানীয় ভেরিয়েবল যুক্ত করার ফলে সমস্যাটি সমাধান হয়েছে, একটি অবজেক্ট রেফারেন্সটি 4 বাইট তাই এটি ডাবল ভেরিয়েবলটিকে 4 দ্বারা সরিয়েছে , এখন এটি প্রান্তিককরণ করে। ভুলবসত.

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

নেট নেট জিটারগুলি সিমডি নির্দেশাবলী সহজে সমর্থন করে না এর সাথে এটি অন্তর্নিহিত সমস্যা। তাদের অনেক বেশি শক্তিশালী প্রান্তিককরণের প্রয়োজনীয়তা রয়েছে, প্রসেসর নিজেই এটি সমাধান করতে পারে না। এসএসই 2 এর 16 টি প্রান্তিককরণ প্রয়োজন, এভিএক্সের 32 টি প্রান্তিককরণ প্রয়োজন managed এটি পরিচালিত কোডটিতে পাওয়া যায় না।

সর্বশেষে তবে সর্বনিম্ন নয়, এটিও নোট করুন যে এটি সি # প্রোগ্রামের পারফেক্টটিকে 32-বিট মোডে চালিত করে খুব অনির্দেশ্য। আপনি যখন কোনও বস্তু হিসাবে ক্ষেত্র হিসাবে সঞ্চিত একটি দ্বিগুণ বা দীর্ঘ অ্যাক্সেস করেন তখন আবর্জনা সংগ্রহকারী গাদাটি সংযোগ করে তখন পার্ফ মারাত্মকভাবে পরিবর্তন করতে পারে। যা বস্তুগুলিকে মেমোরিতে স্থানান্তরিত করে, এমন ক্ষেত্রটি হঠাৎই হঠাৎ ভুল / সারিবদ্ধ হতে পারে। অবশ্যই খুব এলোমেলো, বেশ হেড স্ক্র্যাচার হতে পারে :)

ভাল, কোনও সাধারণ ফিক্স ছাড়া একটি, 64৪-বিট কোড ভবিষ্যত। মাইক্রোসফ্ট প্রজেক্টের টেমপ্লেটটি পরিবর্তন না করবে ততক্ষণ জিরো জোর করে উঠুন Remove সম্ভবত পরবর্তী সংস্করণ যখন তারা রিউজিৎ সম্পর্কে আরও আত্মবিশ্বাসী বোধ করবেন।


4
ডাবল ভেরিয়েবল (এবং টেস্ট 2-এ) নিবন্ধভুক্ত হতে পারে এমন ক্ষেত্রে কীভাবে প্রান্তিককরণ তৈরি হয় তা নিশ্চিত নন। টেস্ট 1 স্ট্যাকটি ব্যবহার করে, টেস্ট 2 এটি ব্যবহার করে না।
usr

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

4
ওম, এটা সত্য। কেন পদ্ধতি সারিবদ্ধকরণ উত্পন্ন নির্দেশাবলী উপর কোন প্রভাব ফেলবে ?! লুপ শরীরের জন্য কোনও পার্থক্য থাকা উচিত নয়। সমস্ত রেজিস্টারে থাকা উচিত। প্রান্তিককরণ অগ্রভাগ অপ্রাসঙ্গিক হওয়া উচিত। এখনও জেআইটি বাগের মতো মনে হচ্ছে।
usr

4
আমাকে উত্তরটি উল্লেখযোগ্যভাবে সংশোধন করতে হবে, বুমার। আমি আগামীকালের মধ্যে এটি পেতে হবে।
হান্স প্যাস্যান্ট

4
@ হ্যানসপাসান্ট আপনি কি জেআইটি সূত্র ধরে খোঁজ নিতে যাচ্ছেন? মজা হবে। এই মুহুর্তে আমি কেবল জানি এটি একটি এলোমেলো জেআইটি বাগ।
usr ডিরেক্টরির

5

এটিকে কিছুটা সঙ্কুচিত করে (কেবল 32-বিট সিএলআর 4.0 রানটাইমকে প্রভাবিত করে)।

লক্ষ্য রাখুন যে var f = Stopwatch.Frequency;সমস্ত পার্থক্য তৈরি করে।

ধীর (2700 মিমি):

static void Test1()
{
  Point a = new Point(1, 1), b = new Point(1, 1);
  var f = Stopwatch.Frequency;

  var sw = Stopwatch.StartNew();
  for (int i = 0; i < ITERATIONS; i++)
    a = AddByVal(a, b);
  sw.Stop();

  Console.WriteLine("Test1: x={0} y={1}, Time elapsed: {2} ms",
      a.X, a.Y, sw.ElapsedMilliseconds);
}

দ্রুত (800 মি)

static void Test1()
{
  var f = Stopwatch.Frequency;
  Point a = new Point(1, 1), b = new Point(1, 1);

  var sw = Stopwatch.StartNew();
  for (int i = 0; i < ITERATIONS; i++)
    a = AddByVal(a, b);
  sw.Stop();

  Console.WriteLine("Test1: x={0} y={1}, Time elapsed: {2} ms",
      a.X, a.Y, sw.ElapsedMilliseconds);
}

স্পর্শ না করে কোড Stopwatchপরিবর্তন করা গতিতেও মারাত্মক পরিবর্তন করে। পদ্ধতির স্বাক্ষরটি পরিবর্তিত করা Test1(bool warmup)এবং Consoleআউটপুটটিতে শর্তসাপেক্ষ যুক্ত করা : if (!warmup) { Console.WriteLine(...); }একই প্রভাব রয়েছে (ইস্যুটিকে পুনরায় প্রতিস্থাপনের জন্য আমার পরীক্ষাগুলি তৈরি করার সময় এটিতে হোঁচট খেয়েছে)।

@ ইনবিটউইন: আমি দেখেছি, কিছু মৎসযুক্ত। এছাড়াও কেবল স্ট্রাক্টগুলিতে ঘটে।
লেপি

4

জিটরে কিছু ত্রুটি রয়েছে বলে মনে হচ্ছে কারণ আচরণটি আরও খারাপ। নিম্নলিখিত কোড বিবেচনা করুন:

public static void Main()
{
    Test1(true);
    Test1(false);
    Console.ReadLine();
}

public static void Test1(bool warmup)
{
    Point a = new Point(1, 1), b = new Point(1, 1);

    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < ITERATIONS; i++)
        a = AddByVal(a, b);
    sw.Stop();

    if (!warmup)
    {
        Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms",
            a.X, a.Y, sw.ElapsedMilliseconds);
    }
}

এটি 900এমএসে চলবে , বহিরাগত স্টপওয়াচের ক্ষেত্রে একই। তবে, আমরা যদি if (!warmup)শর্তটি সরিয়ে ফেলি তবে এটি 3000এমএসে চলবে । এমনকি অপরিচিত কী, নিম্নলিখিত কোডটি 900এমএসেও চলবে :

public static void Test1()
{
    Point a = new Point(1, 1), b = new Point(1, 1);

    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < ITERATIONS; i++)
        a = AddByVal(a, b);
    sw.Stop();

    Console.WriteLine("Result: x={0} y={1}, Time elapsed: {2} ms",
        0, 0, sw.ElapsedMilliseconds);
}

নোট আমি আউটপুট থেকে সরিয়েছি a.Xএবং a.Yরেফারেন্স Console

কী চলছে তা আমার কোনও ধারণা নেই, তবে এটি আমার কাছে সুন্দর বগির গন্ধ পেয়েছে এবং এটি বাইরের থাকার Stopwatchবা না থাকার সাথে সম্পর্কিত নয়, বিষয়টি কিছুটা আরও সাধারণীর্ণ বলে মনে হচ্ছে।


আপনি যখন কলগুলিতে সরিয়ে ফেলেন a.Xএবং a.Yসংকলক সম্ভবত লুপের অভ্যন্তরে প্রায় সমস্ত কিছুই অপ্টিমাইজ করতে নিখরচায় থাকবেন, কারণ অপারেশনের ফলাফলগুলি অব্যবহৃত।
গ্রো

@ গ্রু: হ্যাঁ, এটি যুক্তিসঙ্গত বলে মনে হয় তবে আপনি যখন দেখছেন সেই অন্যান্য অদ্ভুত আচরণটি আপনি যখন বিবেচনা করবেন তখন তা নয়। সরানো হচ্ছে a.Xএবং a.Yউপার্জন করা হয় না এটা কোনো তুলনায় যখন আপনি অন্তর্ভুক্ত দ্রুত যেতে if (!warmup)শর্ত বা ওপি এর outerSw, যা দূরে তার নিখুঁত না কিছু বোঝা যায়, তার ঠিক দূর যাই হোক না কেন বাগ (ক দরুণ পর্যাপ্ত গতিতে কোড রান করছে 3000পরিবর্তে MS 900MS)।

4
ওহ, ঠিক আছে, আমি ভেবেছিলাম গতি উন্নতি ঘটেছিল warmupসত্য ছিল, কিন্তু যে ক্ষেত্রে লাইন এমনকি, মুদ্রিত হয় না যদি যেখানে এটা এত নেই আসলে মুদ্রিত রেফারেন্স পেতে a। তবুও আমি নিশ্চিত করতে চাই যে আমি সর্বদা পদ্ধতির শেষের কাছাকাছি কোথাও গণনার ফলাফলগুলি উল্লেখ করছি, যখনই আমি জিনিসগুলি বেঞ্চমার্কিং করছি।
গ্রু
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.