سی شارپ

آیا دیتای بزرگ باعث مرگ سیستم میشه؟

چهار مفهوم، دو محور متفاوت — خیلی‌ها این‌ها رو قاطی می‌کنن. بیایید یه بار برای همیشه مرتبشون کنیم.…

چهار مفهوم، دو محور متفاوت — خیلی‌ها این‌ها رو قاطی می‌کنن. بیایید یه بار برای همیشه مرتبشون کنیم.

محور اول: نحوه‌ی پیمایش (for vs foreach)
این دو فقط syntax نیستن، مکانیزم اجراشون فرق داره.

  • for: index-based، مستقیم روی array/List<T> کار می‌کنه. JIT می‌تونه bounds check رو حذف کنه، پس روی دیتاستِ in-memory معمولاً سریع‌تره.
  • foreach: از enumerator پیمایش می‌کنه، روی هر IEnumerable<T> جواب می‌ده. با List<T> چون enumerator struct هست allocation اضافه نداره، ولی sync هست؛ یعنی کل داده باید از قبل آماده باشه.

محور دوم: کجا داره فیلتر اجرا می‌شه (IQueryable vs IAsyncEnumerable)
اینجا دیگه بحث پیمایش نیست، بحث اینه داده از کجا میاد و کِی پردازش می‌شه.

  • IQueryable: منطق فیلتر (Where, OrderBy...) به Expression Tree تبدیل می‌شه و تا زمان enumerate نشدن اجرا نمی‌شه. وقتی provider (مثل EF Core) اجراش کنه، به SQL ترجمه می‌شه و فقط رکوردهای لازم از دیتابیس میاد.
  • IAsyncEnumerable: اینجا فیلتر معمولاً قبلاً روی سرور/دیتابیس اعمال شده؛ داستان این interface streamingه، نه query translation. نتیجه به‌صورت async، رکورد به رکورد، بدون این‌که منتظر کل دیتاست بمونی، به دستت می‌رسه.

نکته‌ی مهم: این دو تا مکمل هم هستن، نه رقیب.
یه جریان معمول این شکلیه:

IQueryable<User> query = dbContext.Users
    .Where(u => u.Age > 30); // هنوز روی DB اجرا نشده

await foreach (var user in query.AsAsyncEnumerable())
{
    Process(user); // هر رکورد همون لحظه‌ای که از DB میاد پردازش می‌شه
}

اول با IQueryable فیلتر رو روی دیتابیس می‌بری (SQL WHERE)، بعد با IAsyncEnumerable نتیجه رو به‌صورت stream می‌خونی، بدون این‌که کل نتیجه رو یکجا توی List بریزی.

جمع‌بندی روی یک جدول

for foreach IQueryable IAsyncEnumerable
نوع حلقه (پیمایش) حلقه (پیمایش) نوع داده (کوئری) نوع داده (استریم)
منبع مناسب array/List در حافظه هر IEnumerable دیتابیس (EF Core) هر منبع async (DB, API, فایل)
زمان اجرای فیلتر - - روی دیتابیس، هنگام enumerate معمولاً از قبل روی سرور اعمال شده
مصرف حافظه کم (دیتای کوچیک) کم (دیتای کوچیک) کم (فقط نتیجه‌ی فیلترشده) خیلی کم (رکورد به رکورد)
بلاک‌کننده؟ بله (sync) بله (sync) تا enumerate نشه اجرا نمی‌شه نه، async

قانون ساده: برای دیتای کوچیک و در حافظه، بین for و foreach بر اساس خوانایی کد انتخاب کن، تفاوت performance ناچیزه. برای دیتای دیتابیسی، همیشه IQueryable رو تا آخرین لحظه نگه دار تا فیلتر به SQL ترجمه بشه؛ و وقتی حجم نتیجه بالاست یا منبع async هست، IAsyncEnumerable رو برای مصرف نتیجه به‌کار ببر.

#dotnet #csharp #performance #asynchronous

نوشته‌شده توسط محمد نظری من اینجا هستم تا دانشم رو با شما به اشتراک بذارم.

دیدگاه‌ها 0

برای ارسال دیدگاه یا پسند، وارد شوید.

ورود / ثبت‌نام

هنوز دیدگاهی نیست — اولین نفر باشید.