در دنیای پردازش مدرن، بهینهسازی عملکرد از اهمیت بالایی برخوردار است. گاهی اوقات، مشکلات عملکردی در لایههای عمیق سختافزار و سیستمعامل پنهان میشوند و شناسایی آنها نیازمند دانش فنی دقیق است. یکی از این موارد، مشکل کاهش عملکرد ناشی از دستورالعملهای اتمیک (atomic) در پردازندههای ARM64، بهویژه در سیپییوهای Ampere است که منجر به پدیدهای به نام «خطای صفحه دوگانه» (double page fault) میشود.
این مقاله به بررسی فنی این مشکل، علت ریشهای آن و راهحلهای عملی برای رفع آن میپردازد. درک این مسئله برای توسعهدهندگان و مدیران سیستمی که با معماری Arm کار میکنند، بسیار حیاتی است.
برای درک عمق مشکل، ابتدا باید با دو مفهوم کلیدی آشنا شویم. دستورالعملهای اتمیک عملیاتی هستند که به صورت یک واحد کامل و غیرقابل تقسیم اجرا میشوند. این ویژگی در برنامهنویسی چندنخی (multi-threaded) برای جلوگیری از تداخل و حفظ یکپارچگی دادهها ضروری است. برای مثال، وقتی چند نخ به طور همزمان قصد افزایش یک شمارنده را دارند، یک عملیات اتمیک تضمین میکند که این افزایش به درستی و بدون از دست رفتن داده انجام شود.
از سوی دیگر، «خطای صفحه» یا Page Fault یک استثنا (exception) است که توسط سختافزار مدیریت حافظه (MMU) ایجاد میشود. این خطا زمانی رخ میدهد که یک برنامه تلاش میکند به صفحهای از حافظه مجازی دسترسی پیدا کند که در حال حاضر در حافظه فیزیکی (RAM) بارگذاری نشده است. در این حالت، سیستمعامل وارد عمل شده، صفحه مورد نظر را از دیسک به حافظه منتقل کرده و سپس اجرای برنامه را از سر میگیرد.
مشکل اصلی در پردازندههای Ampere با معماری ARM64 این است که اجرای برخی دستورالعملهای اتمیک خاص منجر به ایجاد دو خطای صفحه متوالی برای یک دسترسی به حافظه میشود. این رفتار غیرمنتظره، هزینههای سنگینی را به سیستم تحمیل میکند. هر خطای صفحه نیازمند دخالت هسته سیستمعامل و انجام عملیات ورودی/خروجی است که فرآیندی زمانبر محسوب میشود. حال تصور کنید این فرآیند برای یک دستورالعمل، دو بار تکرار شود.
این پدیده تأثیر مخرب دیگری نیز دارد: تکهتکه شدن (fragmentation) صفحات بزرگ حافظه (huge pages).
صفحات بزرگ (Huge Pages) یکی از مکانیزمهای بهینهسازی عملکرد در سیستمعاملهای مدرن هستند. به جای استفاده از صفحات استاندارد ۴ کیلوبایتی، سیستمعامل میتواند از صفحات بزرگتر (مثلاً ۲ مگابایتی یا ۱ گیگابایتی) استفاده کند. این کار تعداد ورودیهای مورد نیاز در بافر ترجمه آدرس (TLB) را کاهش داده و سرعت دسترسی به حافظه را به شکل قابل توجهی افزایش میدهد.
وقتی یک خطای صفحه دوگانه رخ میدهد، سیستمعامل ممکن است مجبور شود یک صفحه بزرگ را به صفحات کوچکتر ۴ کیلوبایتی تقسیم کند تا بتواند خطا را مدیریت کند. تکرار این فرآیند باعث تکهتکه شدن حافظه و از بین رفتن مزایای عملکردی صفحات بزرگ میشود و در نهایت، عملکرد کلی سیستم را کاهش میدهد.
ریشه این مشکل در نحوه پیادهسازی دستورالعملهای اتمیک خواندن-تغییر-نوشتن (read-modify-write) در این پردازندهها نهفته است. به نظر میرسد این پردازندهها عملیات اتمیک را در دو مرحله جداگانه (یک خواندن و یک نوشتن) مدیریت میکنند. اگر صفحه حافظه مورد نظر در ابتدا در RAM موجود نباشد، مرحله خواندن باعث ایجاد اولین خطای صفحه میشود. پس از آنکه سیستمعامل صفحه را بارگذاری کرد، مرحله نوشتن تلاش میکند داده را تغییر دهد، اما به دلیل پرچمهای دسترسی (access flags)، ممکن است یک خطای صفحه دیگر ایجاد شود. این رفتار دوگانه، منشأ اصلی افت عملکرد است.
برای مقابله با این مشکل، چندین رویکرد وجود دارد که توسعهدهندگان و مدیران سیستم میتوانند در پیش بگیرند:
در نهایت، شناسایی و حل چنین مشکلات عمیقی نشاندهنده اهمیت همکاری نزدیک میان طراحان سختافزار، توسعهدهندگان سیستمعامل و برنامهنویسان است. با درک دقیق تعاملات میان این لایهها، میتوان به سیستمهایی با پایداری و عملکرد بالاتر دست یافت.