عنوان سخنرانی: Data-Oriented Design in Practice
سخنران: Andrew Kelley (رئیس و توسعهدهنده ارشد نرمافزار بنیاد Zig)
لینک ویدیو: https://youtu.be/IroPQ150F6c?si=f9EH5p-AalCJeQOyتاریخ ارائه: 2024
Andrew Kelley، رئیس و توسعهدهنده ارشد نرمافزار بنیاد ZIG ، در این سخنرانی به بررسی اصول طراحی دادهمحور و کاربرد عملی آنها در بهینهسازی کامپایلر میپردازد. او با بیان داستان شخصی خود از یادگیری برنامهنویسی و مواجهه با مفاهیم طراحی دادهمحور، سفر خود را از یک توسعهدهنده مبتدی تا درک عمیق این رویکرد روایت میکند.
سخنرانی با یک درس اساسی درباره معماری کامپیوتر آغاز میشود: پردازنده سریع است اما حافظه اصلی کند است. تفاوت سرعت بین حافظه نهان سطح اول (و حافظه اصلی چندین مرتبه بزرگی است. این شکاف عملکردی، انگیزه اصلی طراحی دادهمحور، کاهش دسترسی به حافظه با فشردهسازی دادهها و بهبود محلیت مرجع را شکل میدهد: .
بخش تعاملی سخنرانی، نحوه چیدمان ساختارها در حافظه را بررسی میکند. مفاهیم تراز طبیعی، padding، و اندازه ساختارها با مثالهای عملی توضیح داده میشوند. برای نمونه، یک ساختار با دو فیلد u32 و یک فیلد boolean، بدون بهینهسازی ۸ بایت و با چیدمان بهینه ۵ بایت اشغال میکند. در مثالی دیگر، یک ساختار با سه فیلد u32، u64، u32، ۲۴ بایت اشغال میکند، اما با جابجایی فیلدها، اندازه آن به ۱۶ بایت کاهش مییابد.
پنج استراتژی عملی برای کاهش ردپای حافظه معرفی میشوند. نخست، استفاده از شاخصها به جای اشارهگرها که در پردازندههای ۶۴ بیتی، اندازه هر فیلد را از ۸ بایت به ۴ بایت کاهش میدهد. دوم، ذخیره دادههای بولی خارج از باند با استفاده از آرایههای جداگانه برای موجودیتهای زنده و مرده که هم حافظه و هم بررسیهای شرطی در حلقهها را کاهش میدهد. سوم، استفاده از آرایههای سازنده به جای آرایهای از ساختارها که padding بین عناصر را حذف میکند. چهارم، ذخیره دادههای پراکنده در hashmapها برای مواردی که اکثر موجودیتها مقدار خاصی ندارند. و پنجم، استفاده از رویکرد کدگذاری به جای چندریختی که با کدگذاری برچسبها و دادههای مشترک در ساختارهای متفاوت، اندازه هر موجودیت را به طور چشمگیری کاهش میدهد.
مطالعه موردی کامپایلر زیگ نشان میدهد که این استراتژیها در دنیای واقعی مؤثر هستند. اندازه هر توکن از ۶۴ بایت به ۵ بایت کاهش یافت که کاهشی ۹۲ درصدی را نشان میدهد. زمان اجرای کل فرایند توکنسازی ۲۲ درصد سریعتر شد. اندازه گرههای درخت نحو از میانگین ۵۴ بایت به ۲۰.۳ بایت کاهش یافت که به بهبود ۳۹ درصدی در زمان اجرا منجر شد. با موازیسازی بخشهایی از خط لوله، سرعت پردازش به ۸.۹ میلیون خط کد در ثانیه رسید. خروجی این بخش از خط لوله اکنون فقط چهار آرایه است که به راحتی با یک فراخوان سیستمی روی دیسک ذخیره یا بارگذاری میشود.

این سخنرانی برای من بسیار جالب و پر از نکات جدید بود. اولین و مهمترین نکته، تفاوت چشمگیر سرعت بین حافظه نهان و حافظه اصلی بود. این واقعیت که انجام محاسبات ریاضی از خواندن از حافظه L1 نیز سریعتر است، یک دیدگاه کاملاً جدید به من داد. این یعنی گاهی بهتر است یک مقدار را دوباره محاسبه کنیم تا اینکه آن را در حافظه ذخیره و بازیابی کنیم.
دومین نکته جالب، مفهوم padding و نحوه تأثیر ترتیب فیلدها بر اندازه ساختارها بود. مثالی که یک boolean یک بیتی باعث افزایش ۸ بایتی اندازه ساختار میشود (از ۱۶ به ۲۴ بایت)، بسیار شگفتانگیز بود. این نشان میدهد که طراحی ساده دادهها چقدر میتواند بر مصرف حافظه تأثیر بگذارد.
سومین نکته، استراتژیهای عملی مانند استفاده از آرایههای سازنده به جای آرایهای از ساختارها بود. این ترفند ساده padding را حذف میکند و علاوه بر کاهش حافظه، محلیت مرجع را نیز بهبود میبخشد. همچنین ذخیره دادههای پراکنده در hashmap و ذخیره بولیها خارج از باند، ایدههای خلاقانهای بودند که نشان میدهند چگونه میتوان دادهها را بر اساس الگوهای دسترسی سازماندهی کرد.
چهارمین نکته، رویکرد کدگذاری به جای چندریختی بود. کدگذاری برچسبها و دادههای مشترک در ساختارهای مختلف، به جای استفاده از اتحادیههای برچسبدار، اندازه هر موجودیت را به طور چشمگیری کاهش میدهد. این رویکرد نشان میدهد که با شناخت دقیق از دادهها و الگوهای دسترسی، میتوان طراحیهای بسیار کارآمدتری ایجاد کرد.
در نهایت، نتایج بهدستآمده در کامپایلر زیگ یعنی کاهش ۹۲ درصدی اندازه توکنها و بهبود ۳۹ درصدی زمان اجرا، نشان میدهد که این تکنیکها در دنیای واقعی و در پروژههای بزرگ به خوبی عمل میکنند.
این سخنرانی ارتباط عمیقی با مباحث درسی معماری نرمافزار و سیستمهای کامپیوتری دارد. اولین ارتباط، معماری حافظه و سلسلهمراتب حافظه نهان است. درک این که دسترسی به حافظه اصلی چندین مرتبه کندتر از محاسبات ریاضی است، یکی از مفاهیم کلیدی در طراحی سیستمهای با کارایی بالا محسوب میشود. این موضوع در درسهای معماری کامپیوتر و سیستمهای عامل به تفصیل بررسی میشود.
ارتباط دوم، الگوهای طراحی دادهمحور است. این رویکرد با الگوی سنتی شیءگرا که بر رفتار و کپسولهسازی تأکید دارد، تفاوت اساسی دارد. طراحی دادهمحور بر سازماندهی دادهها بر اساس الگوهای دسترسی و ویژگیهای سختافزاری تمرکز میکند.
ارتباط سوم، مدیریت حافظه و تخصیص است. استفاده از آرایههای سازنده و شاخصها به جای اشارهگرها، تأثیر مستقیمی بر نحوه تخصیص و دسترسی به حافظه دارد.
ارتباط چهارم، تعادل بین قابلیت نگهداری و عملکرد است. رویکرد کدگذاری که در کامپایلر زیگ استفاده شده، قابلیت نگهداری کد را تا حدی کاهش میدهد اما عملکرد را به طور چشمگیری بهبود میبخشد. این مصالحه، یکی از چالشهای اصلی در معماری نرمافزار به شمار میرود.
با وجود موفقیتهای چشمگیر، چند سوال و نکته انتقادی برای من ایجاد شد. سوال اول این است که رویکرد کدگذاری که اندازه گرهها را به شدت کاهش میدهد، چقدر بر قابلیت نگهداری و خوانایی کد تأثیر میگذارد؟ آیا این رویکرد برای تیمهای بزرگ که نیاز به همکاری دارند، مناسب است؟ سخنران اشاره کرد که کدگذاریها بر اساس توزیعهای واقعی دادهها طراحی میشوند، اما تغییر این کدگذاریها در آینده چقدر دشوار خواهد بود؟
سوال دوم اینکه آیا این تکنیکها برای همه نوع برنامهها قابل تعمیم هستند؟ به نظر میرسد این رویکرد بیشتر برای سیستمهایی که تعداد زیادی شیء از یک نوع دارند و الگوهای دسترسی مشخصی دارند، مناسب است. برای برنامههایی با ساختار دادههای پیچیده و پویا، آیا این تکنیکها همچنان مؤثر خواهند بود؟
سوال سوم اینکه هزینه پیادهسازی این بهینهسازیها چقدر است؟ سخنران اشاره کرد که کامپایلر زیگ هنوز در حال انتقال به کد جدید است و تنها ۳۵ درصد از تستهای رفتاری را پاس میکند. آیا این بهینهسازیها ارزش هزینه پیادهسازی و نگهداری را دارند؟ این موضوع به مبحث بازگشت سرمایه در بهینهسازی نرمافزار مربوط میشود.
همچنین، هرچند طراحی دادهمحور مزایای عملکردی چشمگیری دارد، اما این رویکرد بدون محدودیت نیست. پیچیدگی کد، کاهش خوانایی، و دشواری نگهداری در بلندمدت، از جمله چالشهای این رویکرد هستند. بهویژه در تیمهای بزرگ که توسعهدهندگان مختلف روی کد کار میکنند، هزینههای نگهداری ممکن است از مزایای عملکردی بیشتر شود. بنابراین، انتخاب بین طراحی دادهمحور و شیءگرا باید بر اساس نیازمندیهای خاص هر پروژه انجام شود.
این سخنرانی یکی از بهترین ارائههایی بود که در زمینه بهینهسازی عملکرد دیدهام. اندرو کلی با زبانی ساده و مثالهای عملی، مفاهیم پیچیده طراحی دادهمحور را به خوبی توضیح داده است. مهمترین درسی که از این سخنرانی گرفتم، این است که درک عمیق از سختافزار و نحوه عملکرد حافظه، میتواند به بهبودهای شگفتانگیزی در عملکرد نرمافزار منجر شود. حتی در پروژههای بزرگ و پیچیده مانند کامپایلر زیگ، با اعمال تکنیکهای ساده طراحی دادهمحور، میتوان به بهبودهای چشمگیری دست یافت. همچنین، این سخنرانی اهمیت سنجش و اندازهگیری در بهینهسازی را به من یادآوری کرد. سخنران با جمعآوری آمار دقیق و ارائه اعداد ملموس، تأثیر هر تغییر را نشان داد. این رویکرد مبتنی بر داده، یکی از اصول کلیدی مهندسی نرمافزار است که در تمام پروژههای موفق باید رعایت شود.
این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است.
منابع
- Kelley, A. (n.d.). Data-Oriented Design in Practice. Zig Software Foundation. Retrieved from https://youtu.be/IroPQ150F6c?si=f9EH5p-AalCJeQOy
- Fabian, R. (n.d.). Data-Oriented Design.