ফরচ লুপ এবং ভেরিয়েবল ইনিশিয়েশন


11

কোডের এই দুটি সংস্করণের মধ্যে কি পার্থক্য রয়েছে?

foreach (var thing in things)
{
    int i = thing.number;
    // code using 'i'
    // pay no attention to the uselessness of 'i'
}

int i;
foreach (var thing in things)
{
    i = thing.number;
    // code using 'i'
}

বা সংকলক যত্ন করে না? আমি যখন পার্থক্যের কথা বলছি তখন পারফরম্যান্স এবং মেমরির ব্যবহারের ক্ষেত্রে আমি বোঝাতে চাইছি। ..আর মূলত কেবল কোনও পার্থক্য আছে বা সংকলনের পরে দু'টি একই কোড হতে পারে?


6
আপনি কি দুটি সংকলন করে বাইটকোড আউটপুটটির দিকে তাকানোর চেষ্টা করেছেন?

4
@ মিশেলটি আমার কাছে বোধ হয় না আমি বাইটকোড আউটপুট তুলনা করার জন্য যোগ্য .. যদি আমি কোনও পার্থক্য পাই তবে আমি নিশ্চিত নই যে আমি এর সঠিক অর্থটি বুঝতে সক্ষম হয়েছি।
অল্টারনেটেক্স

4
যদি এটি একই হয় তবে আপনার যোগ্য হওয়ার দরকার নেই।

1
@ মিশেলটি যদিও আপনার এই সংকলকটি অপ্টিমাইজ করতে পারে কিনা সে সম্পর্কে একটি ভাল অনুমান করার জন্য পর্যাপ্ত যোগ্যতা অর্জন করার দরকার নেই এবং যদি তা হয় তবে এটি কোন পরিস্থিতিতে এটি সেই অপ্টিমাইজেশন করতে সক্ষম।
বেন অ্যারনসন

@ বেনআরনসন এবং সম্ভবত সেই কার্যকারিতাটি সুদৃ .় করার জন্য একটি তুচ্ছ-তুচ্ছ উদাহরণ প্রয়োজন।

উত্তর:


22

টিএল; ডিআর - তারা আইএল স্তরের সমতুল্য উদাহরণ examples


ডটনেটফিটাল উত্তরটি সুন্দর করে তোলে কারণ এটি আপনাকে ফলস্বরূপ আইএল দেখতে দেয়।

আমার পরীক্ষাটি আরও ত্বরান্বিত করার জন্য আমি আপনার লুপ কনস্ট্রাক্টের কিছুটা আলাদা প্রকরণ ব্যবহার করেছি। আমি ব্যবহার করতাম:

প্রকরণ 1:

using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        int x;
        int i;

        for(x=0; x<=2; x++)
        {
            i = x;
            Console.WriteLine(i);
        }
    }
}

পার্থক্য 2:

        Console.WriteLine("Hello World");
        int x;

        for(x=0; x<=2; x++)
        {
            int i = x;
            Console.WriteLine(i);
        }

উভয় ক্ষেত্রেই সংকলিত আইএল আউটপুট একই রেন্ডার করেছে।

.class public auto ansi beforefieldinit Program
       extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main() cil managed
  {
    // 
    .maxstack  2
    .locals init (int32 V_0,
             int32 V_1,
             bool V_2)
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ldc.i4.0
    IL_000d:  stloc.0
    IL_000e:  br.s       IL_001f

    IL_0010:  nop
    IL_0011:  ldloc.0
    IL_0012:  stloc.1
    IL_0013:  ldloc.1
    IL_0014:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_0019:  nop
    IL_001a:  nop
    IL_001b:  ldloc.0
    IL_001c:  ldc.i4.1
    IL_001d:  add
    IL_001e:  stloc.0
    IL_001f:  ldloc.0
    IL_0020:  ldc.i4.2
    IL_0021:  cgt
    IL_0023:  ldc.i4.0
    IL_0024:  ceq
    IL_0026:  stloc.2
    IL_0027:  ldloc.2
    IL_0028:  brtrue.s   IL_0010

    IL_002a:  ret
  } // end of method Program::Main

সুতরাং আপনার প্রশ্নের উত্তর দেওয়ার জন্য: সংকলকটি ভেরিয়েবলের ঘোষণাকে অনুকূল করে এবং দুটি প্রকরণকে সমতুল্য উপস্থাপন করে।

আমার বোঝার জন্য,। নেট আইএল সংকলকটি ফাংশনের শুরুতে সমস্ত পরিবর্তনশীল ঘোষণাগুলি সরিয়ে নিয়েছে তবে আমি একটি ভাল উত্স খুঁজে পাইনি যা স্পষ্টভাবে বলেছিল যে 2 । এই বিশেষ উদাহরণে, আপনি দেখতে পাচ্ছেন যে এটি তাদের এই বিবৃতিটি দিয়ে সরিয়ে নিয়েছে:

    .locals init (int32 V_0,
             int32 V_1,
             bool V_2)

এর সাথে আমরা তুলনা তৈরিতে কিছুটা বেশি আবেগপ্রবণ হই ....

কেস এ, সমস্ত ভেরিয়েবলগুলি সরানো হবে?

এটি আরও কিছুটা খনন করতে, আমি নিম্নলিখিত ফাংশনটি পরীক্ষা করেছি:

public static void Main()
{
    Console.WriteLine("Hello World");
    int x=5;

    if (x % 2==0) 
    { 
        int i = x; 
        Console.WriteLine(i); 
    }
    else 
    { 
        string j = x.ToString(); 
        Console.WriteLine(j); 
    } 
}

এখানে পার্থক্যটি হ'ল আমরা তুলনার উপর ভিত্তি করে একটি int iবা একটি ঘোষণা করি string j। আবার, সংকলকটি সমস্ত স্থানীয় ভেরিয়েবলগুলি সাথে ফাংশন 2 এর শীর্ষে নিয়ে যায় :

.locals init (int32 V_0,
         int32 V_1,
         string V_2,
         bool V_3)

আমি এটি আকর্ষণীয় মনে করেছি যে int iএই উদাহরণে ঘোষিত না হলেও এটি সমর্থন করার কোডটি এখনও উত্পন্ন হয়েছে।

কেস বি: foreachপরিবর্তে কী হবে for?

এটি চিহ্নিত করা হয়েছিল যে foreachএর চেয়ে আলাদা আচরণ রয়েছে forএবং যে বিষয়ে জিজ্ঞাসা করা হয়েছিল আমি সেই একই জিনিসটি যাচাই করছিলাম না। সুতরাং আমি ফলাফলের আইএল তুলনা করতে কোডের এই দুটি বিভাগে রেখেছি।

int লুপের বাইরে ঘোষণা:

    Console.WriteLine("Hello World");
    List<int> things = new List<int>(){1, 2, 3, 4, 5};
    int i;

    foreach(var thing in things)
    {
        i = thing;
        Console.WriteLine(i);
    }

int লুপের ভিতরে ঘোষণা:

    Console.WriteLine("Hello World");
    List<int> things = new List<int>(){1, 2, 3, 4, 5};

    foreach(var thing in things)
    {
        int i = thing;
        Console.WriteLine(i);
    }

foreachলুপের সাথে ফলাফল প্রাপ্ত আইএল লুপটি ব্যবহার করে উত্পন্ন আইএল থেকে প্রকৃতপক্ষে পৃথক ছিল for। বিশেষত, আর ডি ব্লক এবং লুপ বিভাগটি পরিবর্তিত হয়েছে।

.locals init (class [mscorlib]System.Collections.Generic.List`1<int32> V_0,
         int32 V_1,
         int32 V_2,
         class [mscorlib]System.Collections.Generic.List`1<int32> V_3,
         valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> V_4,
         bool V_5)
...
.try
{
  IL_0045:  br.s       IL_005a

  IL_0047:  ldloca.s   V_4
  IL_0049:  call       instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current()
  IL_004e:  stloc.1
  IL_004f:  nop
  IL_0050:  ldloc.1
  IL_0051:  stloc.2
  IL_0052:  ldloc.2
  IL_0053:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0058:  nop
  IL_0059:  nop
  IL_005a:  ldloca.s   V_4
  IL_005c:  call       instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()
  IL_0061:  stloc.s    V_5
  IL_0063:  ldloc.s    V_5
  IL_0065:  brtrue.s   IL_0047

  IL_0067:  leave.s    IL_0078

}  // end .try
finally
{
  IL_0069:  ldloca.s   V_4
  IL_006b:  constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
  IL_0071:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
  IL_0076:  nop
  IL_0077:  endfinally
}  // end handler

foreachপদ্ধতির আরো স্থানীয় ভেরিয়েবল তৈরি এবং কিছু অতিরিক্ত শাখাবিন্যাস প্রয়োজন। মূলত, এটির প্রথমবারের মধ্যে অঙ্কের প্রথম পুনরাবৃত্তি পেতে লুপের শেষের দিকে লাফ দেয় এবং তারপরে লুপ কোডটি কার্যকর করতে লুপের প্রায় শীর্ষে ফিরে যায়। এটি তখন আপনার প্রত্যাশা মতো লুপ অবিরত থাকবে।

কিন্তু ব্যবহার দ্বারা সৃষ্ট শাখাবিন্যাস পার্থক্য পরলোক forএবং foreachনির্মান, ছিল কোন যেখানে উপর ভিত্তি করে আইএল পার্থক্য int iঘোষণা স্থাপন করা হয়েছিল। সুতরাং আমরা এখনও সমান দুটি পদ্ধতির এ।

কেস সি: বিভিন্ন সংকলক সংস্করণ সম্পর্কে কি?

একটি মন্তব্য বাম ছিল 1 , একটি একটি লিঙ্ক ছিল foreach এবং অবসান ব্যবহার করে পরিবর্তনশীল অ্যাক্সেস নিয়ে একটি সতর্কবার্তা সংক্রান্ত তাই প্রশ্ন । সেই প্রশ্নটিতে সত্যই যে অংশটি আমার নজর কেড়েছিল তা হ'ল নেট নেট .২.২ সংকলকটি সংকলকের পূর্ববর্তী সংস্করণগুলির বিপরীতে কীভাবে কাজ করেছিল।

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

আমি যে পার্থক্যটি উল্লেখ করেছি তা হল স্থানীয় আরম্ভ ব্লক এবং পরিবর্তনশীল ঘোষণার সাথে। স্থানীয় সংকলকটি ভেরিয়েবলগুলির নামকরণে কিছুটা সুনির্দিষ্ট ছিল।

  .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<int32> things,
           [1] int32 thing,
           [2] int32 i,
           [3] class [mscorlib]System.Collections.Generic.List`1<int32> '<>g__initLocal0',
           [4] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> CS$5$0000,
           [5] bool CS$4$0001)

তবে সেই সামান্যতম পার্থক্যের সাথে, এটি এতদূর, এত ভাল ছিল। আমার ডটনেটফিডলার সংকলক এবং আমার স্থানীয় ভিএস উদাহরণটি যা উত্পাদন করছে তার মধ্যে আমার সমতুল্য আইএল আউটপুট ছিল।

সুতরাং আমি তখন প্রকল্পটি .NET 4, .NET 3.5, এবং ভাল পরিমাপের জন্য পুনরায় তৈরি করেছি N

এবং এই অতিরিক্ত ক্ষেত্রে তিনটি ক্ষেত্রেই উত্পন্ন আইএল সমান ছিল। এই নমুনাগুলিতে উত্পাদিত আইএলটিতে লক্ষ্যযুক্ত .NET সংস্করণটির কোনও প্রভাব ছিল না।


এই দু: সাহসিক কাজটির সংক্ষিপ্তসার হিসাবে: আমি মনে করি আমরা আত্মবিশ্বাসের সাথে বলতে পারি যে সংকলকটি আপনি কোথায় আদিম ধরণের ঘোষণা করেন তা যত্ন করে না এবং ঘোষণাপদ্ধতির কোনও পদ্ধতিতে স্মৃতি বা সম্পাদনের উপর কোনও প্রভাব পড়ে না। এবং যে একটি ব্যবহার নির্বিশেষে সত্য forবা foreachলুপ।

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

1 লুপগুলির মধ্যে foreachসমাপনীকরণের এসও প্রশ্নের মূল লিঙ্ক সরবরাহ করার জন্য অ্যান্ডিকে আপনাকে ধন্যবাদ ।

2 এটি লক্ষণীয় যে ইসিএমএ -৩৩৫ স্পেস এটিকে I.12.3.2.2 বিভাগ 'স্থানীয় ভেরিয়েবল এবং আর্গুমেন্ট' দিয়ে সম্বোধন করে। আমাকে ফলস্বরূপ আইএলটি দেখতে হয়েছিল এবং তারপরে এটি কী চলছে তা সম্পর্কে পরিষ্কার হওয়ার জন্য বিভাগটি পড়তে হয়েছিল। আড্ডায় এটি নির্দেশ করার জন্য র‌্যাচেট ফ্রিককে ধন্যবাদ।


1
কারণ এবং ভবিষ্যদ্বাণী একই আচরণ করে না, এবং প্রশ্নের মধ্যে এমন কোড রয়েছে যা আলাদা হয় যা লুপে বন্ধ হওয়ার পরে গুরুত্বপূর্ণ হয়ে ওঠে। stackoverflow.com/questions/14907987/…
অ্যান্ডি

1
@ অ্যান্ডি - লিঙ্কটির জন্য ধন্যবাদ! আমি এগিয়ে গিয়ে একটি foreachলুপ ব্যবহার করে উত্পন্ন আউটপুট চেক করেছি এবং লক্ষ্যযুক্ত .NET সংস্করণটিও পরীক্ষা করেছিলাম ।

0

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

আপনি যদি প্রতিটি স্থানে একই ধরণের পরিবর্তন শুরু করে থাকেন তবে সংকলকটি একইভাবে লুপের আগে এটি সূচনা করে উল্লেখ করবে।

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


3
আপনার শেষ অনুচ্ছেদটি সত্য কিনা বা না তা দুটি বিষয়ের উপর নির্ভর করে: আপনার নিজের প্রোগ্রামের অনন্য প্রেক্ষাপটে ভেরিয়েবলের সুযোগকে হ্রাস করার গুরুত্ব এবং এটি একাধিক অ্যাসাইনমেন্টগুলি বাস্তবে অপ্টিমাইজ করে কিনা তা সংকলকটির ভিতরে জ্ঞান।
রবার্ট হার্ভে

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

-2

প্রথমে আপনি কেবল লুপের ভিতরে ডিক্লেয়ারিং এবং ইনিশিয়েল করছেন তাই প্রতিবার লুপের লুপগুলি এটি লুপের ভিতরে পুনরায় পুনরায় "i" পাবেন get দ্বিতীয় আপনি কেবল লুপ বাইরে ঘোষণা করছেন।


1
এটি 2 বছর পূর্বে পোস্ট করা শীর্ষস্থানীয় পয়েন্টগুলিতে তৈরি করা এবং ব্যাখ্যা করা হয়েছে এর চেয়ে বেশি কিছু দেওয়ার প্রস্তাব দিচ্ছে না
gnat

2
একটি উত্তর দেওয়ার জন্য আপনাকে ধন্যবাদ, তবে এটি কোনও নতুন দিক স্বীকৃত নয়, শীর্ষ রেট করা উত্তর ইতিমধ্যে কভার করে না (বিশদে)।
CharonX
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.