تفاوت حلقه for و foreach در چیست؟ مقایسه for و each در سی شارپ

For و Foreach تفاوت کمی در Performance دارند و سرعت آنها تقریبا برابر است. اما حلقه Foreach از فضای پشته بیشتری برای متغیرهای محلی استفاده می کند. در این مقایسه سعی میکنیم تفاوت دقیق این دوحلقه را بفهمیم.

دوره های شبکه، برنامه نویسی، مجازی سازی، امنیت، نفوذ و ... با برترین های ایران

مقایسه حلقه for و each

ابتدا نگاهی به حلقه For (متد1) می اندازیم و پس از آن حلقه Foreach (متد2) هر دو متد مجموع یک آرایه را بر می گردانند.

For loop version: Method1

static int Method1(int[] array)
{
    int a = 0;
    for (int i = 0; i < array.Length; i++)
    {
	a += array[i];
    }
    return a;
}

Foreach loop version: Method2

static int Method2(int[] array)
{
    int a = 0;
    foreach (int value in array)
    {
	a += value;
    }
    return a;
}

تفاوت حلقه for و each

در دات نت 4 یک تفاوت بین این دو در IL (Intermediate Language) وجود دارد. در Method1 از فضای پشته کافی برای دو متغیر استفاده شده است. (متغیر a و i). Method2 فضای پشته برای چهار متغیر استفاده می کند (a، value و دو متغیر موقت ساخته شده توسط کامپایلر) وقتی یک متد در CLR (Common Language Runtime) فراخوانی می شود تمام حافظه مورد نیاز متغیرهای محلی بر روی پشته اختصاص داده می شود و به این دلیل که این اختصاص فضا داخل پشته انجام میگیرد سریعتر می باشد اما این عمل بدون هزینه نیست. بنابراین حلقه Foreach فضایی بیش از دو متغیر در نمونه یاد شده اشغال می کند. حالا این دو متد را آزمایش می کنیم.

کد 1:

using System;
using System.Diagnostics;

class Program
{
    static void Main()
    {
	int[] array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	Method1(array);
	Method2(array);

	var s1 = Stopwatch.StartNew();
	const int m = 100000000;
	for (int i = 0; i < m; i++)
	{
	    Method1(array);
	}
	s1.Stop();
	var s2 = Stopwatch.StartNew();
	for (int i = 0; i < m; i++)
	{
	    Method2(array);
	}
	s2.Stop();
	Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds *
	    1000000) / m).ToString("0.00 ns"));
	Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds *
	    1000000) / m).ToString("0.00 ns"));
	Console.Read();
    }
}

*خروجی

7.37 ns

8.04 ns*

نتیجه حلقه for سریعتر از حلقه Foreach می باشد.

کد 2:

متغیرهای محلی اضافی موجود در حلقه Foreach باعث کند شدن این حلقه در حالت ساده آن می شوند. هر چند که این امکان وجود دارد تا از این متغیرهای اضافی به سود خودمان استفاده کنیم.حالا متدهای بالا را را تغییر می دهیم و به جای دسترسی به آرایه ازطریق یک عبارت، یک بار دیگر آن عبارت را تکرار میکنیم. در حقیقت ما دوبار در حلقه به آرایه دسترسی پیدا میکنیم.

متد 1

static int Method1(int[] array)
{
    int a = 0;
    for (int i = 0; i < array.Length; i++)
    {
	a += array[i];
	a += array[i];
    }
    return a;
}

متد 2

static int Method2(int[] array)
{
    int a = 0;
    foreach (int value in array)
    {
	a += value;
	a += value;
    }
    return a;
}

کد اصلی را دوباره با متدهای جدید اجرا میکنیم.

*خروجی

7.71 ns

7.69 ns*

نتیجه جدید

حلقه for به میزان کمی از حلقه foreach کندتر عمل کرد. این بدین خاطر است که متغیرهای محلی که مقادیر عناصر آرایه را در خود ذخیره میکنند سریعتر از دسترسی به مقادیر ازطریق اندیس ها می باشند.

جمع بندی

معرفی متغیرهای اضافی در حلقه های foreach می تونه performance رو تحت تاثیر قرار بده. هر چند اگر از این متغیر ها به دفعات در یک حلقه استفاده شود می تواند عاملی برای بهبود performance باشد.بنابراین حلقه for در زمانی که فقط یک بار به یک آرایه از طریق اندیس آن دسترسی داسته باشیم سریعتر می باشد.

استفاده از Lambda Foreach

Lambda Foreach عمکردی بسیار بهتری نسبت به Foreach معمولی دارد.در مثال زیر ما لیستی از اعداد را با استفاده از سه روش پیاده سازی می کنیم:

روش اول: استفاده از دستور Foreach

var ListOfNumber = new List<int>() { 100, 200, 300, 400, 500 };
foreach (var number in ListOfNumber)
{
       Console.WriteLine(number);
}

روش دوم: استفاده از Lambda ForEach (تولید شده برای IEnumeratorها)

var ListOfNumber = new List<int>() { 100, 200, 300, 400, 500 };
ListOfNumber.ForEach(number => 
{ 
       Console.WriteLine(number); 
}
);

روش سوم : استفاده از متد ForEach در آرایه

int[] arrayOfNumbers = new int[] { 100, 200, 300, 400, 500 };
Array.ForEach<int>(arrayOfNumbers, (int counter) => 
{ 
       Console.WriteLine(counter); 
}
);

نتیجه اجرای دستورات بالا نشون میده که بیشترین سرعت رو روش دوم داره و در کل استفاده از Lambda Foreach از دستور Foreach سرعت بالاتری دارد.روش سوم و استفاده از Array.Foreach سرعت کمتری نسبت به List.Foreach دارد و دلیل آن هم اینه که کامپایلر هنگام کار با آرایه‌ها و اجرای اون‌ها به صورت حلقه، کد IL خاصی رو تولید می‌کنه که مخصوص کار با آرایه هاست و سرعت اون به مراتب از سرعت کد IL تولید شده برای IEnumeratorها پایین تره.

موفق باشید

منابع:

MSDN

DotNetPerls


نظرات