اگر مدلهای زبانی را بهصورت محلی اجرا میکنید، سرعت تولید متن شما بهزودی جهشی خیرهکننده خواهد داشت. عدد ۱۴۰ برابر، همان جهشی است که اخیراً در سرعت پیشنویس رمزگشایی (Drafting Speed) برای جستوجوی پرامپت در لاماسیپلاسپلاس (llama.cpp) ثبت شده است. این جهش عظیم در عملکرد، نتیجهی یک سری جراحیهای دقیق روی نحوه مدیریت حافظه و جایگزینی ساختارهای دادهای کند کتابخانه استاندارد با گزینههایی است که با حافظه پنهان (Cache) پردازنده سازگارترند.
رمزگشایی گمانهزنانه (Speculative Decoding) یا آنچه اغلب «رمزگشایی جستوجوی پرامپت» (Prompt Lookup Decoding) نامیده میشود، تکنیکی است که توسط موتورهای استنتاجی مانند vLLM و Hugging Face Transformers برای افزایش سرعت تولید توکنها به کار میرود. این روش در واقع از یک مدل ساده n-gram به عنوان یک «مدل پیشنویس» (Draft Model) استفاده میکند تا چند توکن بعدی را پیشبینی کند. سپس مدل زبانی بزرگتر (LLM) این پیشبینیها را در یک گذر واحد (Single Pass) تأیید میکند. اگر پیشنویس درست باشد، مدل در هر گذر پیشرو (Forward Pass) چندین توکن تولید میکند که این امر تأخیر (Latency) را بهطور چشمگیری کاهش میدهد. این بهینهسازیها در راستای تسهیل اجرای مدلهای بهینه روی سختافزارهای مختلف است، مشابه آنچه در یکپارچگی مدلهای GGUF با هستههای ggml برای بهبود دسترسی به مدلها مشاهده کردیم.
همانطور که در تحلیلهای قبلی ما دربارهی بهینهسازی استنتاج در لبه اشاره کردیم، هر میلیثانیه در لایههای پایین سیستم اهمیت دارد. در ۲۶ سپتامبر ۲۰۲۶، توسعهدهندهای به نام Hayder Tirmazi مجموعهای از بهینهسازیها را به تفصیل شرح داد که در ابتدا سرعت پیشنویس را ۴۲ برابر افزایش داد. این بهبودها بعدها با یک درخواست ادغام (Pull Request) از سوی Daniel Lemire تقویت شد و مجموع سرعت را به ۱۴۰ برابر رساند. این تغییرات سه نوع حافظه موقت n-gram مورد استفاده در llama.cpp را هدف قرار داد: حافظه زمینه (Context Cache) برای توکنهای فعلی، حافظه پویا (Dynamic Cache) برای اجراهای قبلی، و حافظه ایستا (Static Cache) که از پیش از روی یک مجموعه داده (Corpus) ساخته شده است.
رفع باگ کپیبرداری
به نقل از مستندات پروژه، اولین و فوریترین دستاورد از رفع یک باگ ساده حاصل شد. Tirmazi متوجه شد که نقشههای داخلی (Inner Maps) در هر مرحله از پیشنویس بهطور غیرضروری کپی میشوند. با تغییر کد برای خواندن این نقشهها بهصورت ارجاعی (By Reference)، سرعت پیشنویس بسته به اندازه مجموعه داده، بین ۴.۵ تا ۲۵.۶ برابر افزایش یافت.
جایگزینی نقشه بیرونی
llama.cpp در ابتدا از std::unordered_map برای حافظههای موقت n-gram خود استفاده میکرد. این پیادهسازی کتابخانه استاندارد بهدلیل استفاده از زنجیرهسازی لیستهای پیوندی (Linked-list Chaining) برای مدیریت برخوردها (Collisions)، بهطور بدنامی کند است زیرا با حافظه پنهان CPU سازگار نیست.
Tirmazi نقشه بیرونی را با ankerl::unordered_dense::segmented_map جایگزین کرد. این تغییر چندین مزیت کلیدی به همراه داشت:
- سرعت بارگذاری حافظه ایستا ۱.۴۱ تا ۱.۶۵ برابر بیشتر شد.
- سرعت پیشنویس ۱.۰۲ تا ۱.۱۳ برابر بهبود یافت.
- مصرف حافظه برای حافظه ایستا ۱.۰۷ تا ۱.۱۱ برابر کاهش یافت.
انتخاب نسخه segmented_map بهطور خاص برای جلوگیری از جهشهای ناگهانی مصرف حافظه (Memory Spikes) صورت گرفت که معمولاً هنگام دوبرابر شدن اندازه بردارها (Vector Doubling) در مجموعهدادههای بزرگ رخ میدهد.
بهینهسازی نقشه داخلی
بسیاری از n-gramها دنبالکنندههای (Followers) بسیار کمی دارند، به همین دلیل استفاده از یک نقشه هش (Hash Map) کامل برای هر ورودی داخلی، اتلافی بزرگ است. Tirmazi دریافت که ۶۴٪ از ۲-gramها در مجموعه داده WikiText-103 تنها یک دنبالکننده دارند.
او نقشه داخلی std::unordered_map را با یک std::vector مرتبشده جایگزین کرد. برای جلوگیری از افزایش تأخیر در جستوجوی n-gramهای پرتکرار (که میتوانند هزاران دنبالکننده داشته باشند)، او یک جستوجوی دودویی (Binary Search) با طول ثابت پیاده کرد. برخلاف std::lower_bound در کتابخانه استاندارد، این حلقه سفارشی، بررسی طول را از مقایسه جدا میکند و به CPU اجازه میدهد خواندن حافظه را بهطور مؤثرتری در خط لوله (Pipeline) قرار دهد.
این تغییر باعث شد سرعت پیشنویس بدون حافظه ایستا ۲.۰۹ برابر و با حافظه ایستا تا ۱.۲۵ برابر افزایش یابد، در حالی که مصرف حافظه پیک (Peak Memory) را تقریباً ۲ برابر کاهش داد.
انتقال به Constmap
بهدلیل اینکه حافظه ایستا پس از بارگذاری تغییرناپذیر (Immutable) است، Tirmazi ساختار constmap را پیاده کرد؛ ساختاری که بر اساس فیلترهای فیوز دودویی (Binary Fuse Filters) توسعهیافته توسط Daniel Lemire است.
در این پیکربندی، constmap یک مقدار ۶۴ بیتی را ذخیره میکند که هم شامل موقعیت دنبالکنندهها در یک آرایه متصل (Contiguous Array) و هم شامل تعداد آن دنبالکنندهها است. این معماری به موتور اجازه میدهد کل فایل را در یک بافر واحد بخواند و جستوجوها را با کمترین سربار ممکن انجام دهد.
بر اساس بنچمارکهای اجرا شده روی Apple M4 Pro، این تغییر نتایج زیر را به همراه داشت:
- زمان بارگذاری برای یک مجموعه داده ۵۴۱ مگابایتی از ۳.۷۶ ثانیه به ۰.۲۳ ثانیه رسید (افزایش سرعت ۶.۳۲ تا ۱۶.۱۲ برابری).
- حافظه پیک از ۱.۷۱ گیگابایت به ۱.۳۱ گیگابایت کاهش یافت.
- سرعت پیشنویس ۱.۰۶ تا ۱.۲۰ برابر افزایش یافت.
بهینهسازی نهایی آستانه
در نهایت، Daniel Lemire با بهینهسازی بررسیهای آستانه (Threshold Checks)، لایه دیگری از کارایی را اضافه کرد. پیش از این، llama.cpp امتیاز تمام توکنهای کاندید را محاسبه میکرد و سپس بررسی میکرد که آیا آنها به آستانههای مورد نیاز ($a_n$ و $p_n$) رسیدهاند یا خیر.
بهینهسازی Lemire ابتدا پرتکرارترین دنبالکننده را بررسی میکند. اگر کاندید اول شرط آستانه را پاس نکند، موتور تمام کاندیدهای دیگر برای آن n-gram را نادیده میگیرد. همچنین، سیستم ابتدا بررسی میکند که آیا تعداد کل n-gram به حداقل آستانه میرسد یا خیر، و سپس اقدام به امتیازدهی میکند. این تغییر نهایی، سرعت پیشنویس را با حافظه ایستا تا ۴.۲ برابر افزایش داد.
این مجموعه تغییرات، خط پایه (Baseline) استنتاج مدلهای زبانی محلی را تغییر میدهد. تیم توسعه با تبدیل مسئله حافظه موقت n-gram از یک مسئله ساده جستوجو به یک مسئله «محلمندی دادهها» (Data Locality)، ثابت کرد که میتوان بدون تغییر در الگوریتمهای زیربنایی هوش مصنوعی، تنها با اصلاح «لولهکشی» موتور استنتاج، به دستاوردهای عظیم رسید. این رویکرد بهینهسازی لایههای پایین، یادآور تلاشات در معماری ترکیبی ZGCM-1 است که در آن با بهینهسازی موتور اجرا، کارایی مدلهای کوچکتر را به شدت افزایش دادند.
برای توسعهدهندگانی که مدلها بهصورت محلی اجرا میکنند، این بدان معناست که رمزگشایی جستوجوی پرامپت دیگر یک کالای لوکس و پرمصرف از نظر حافظه نیست، بلکه یک پیشفرض بسیار بهینه است. کاهش حافظه پیک و زمان بارگذاری، استفاده از حافظههای ایستا را برای مجموعهدادههای بسیار بزرگتر روی سختافزارهای مصرفکننده توجیهپذیر میکند.
گام بعدی شما
- اگر از مدلهای محلی استفاده میکنید، آخرین Pull Requestهای مخزن GitHub پروژه llama.cpp را دنبال کنید تا این بهینهسازیها را دریافت کنید.
- از ابزار
llama-lookup-statsبرای بنچمارک کردن مجموعهدادههای خود و سنجش میزان بهبود سرعت استفاده کنید. - در صورت استفاده از سختافزارهای Apple Silicon، اثر کاهش زمان بارگذاری را در مدلهای بزرگتر تست کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو