تولید متن در مدل‌های زبانی آن‌طور که از بیرون به نظر می‌رسد نیست. در پشت صحنهٔ هر درخواست، فرآیند پردازش به دو مرحله با ویژگی‌هایی کاملاً متفاوت تقسیم می‌شود: prefill و decode. درک تفاوت میان این دو مشخص می‌کند که چرا بهینه‌سازی استنتاج تا این حد دشوار است.

چرا فرآیند تولید ترتیبی است

مدل‌های زبانی متن را به‌صورت توکن‌به‌توکن تولید می‌کنند و هر توکن جدید به تمامی توکن‌های پیش از خود وابسته است. از آنجا که هر پیش‌بینی، توکن‌های بعدی را تغییر می‌دهد، نمی‌توان توکن بعدی را پیش از تولید توکن فعلی ایجاد کرد. به همین دلیل، فرآیند تولید ذاتاً ترتیبی است و امکان اجرای موازی آن وجود ندارد.

پیش‌پُرکردن: محدود به توان محاسباتی

در مرحلهٔ prefill، مدل تمام پرامپت ورودی را به صورت یک‌جا و موازی پردازش می‌کند. محاسبهٔ هم‌زمان تمام جایگاه‌ها به ضرب‌های ماتریسی بزرگی می‌انجامد که هسته‌های پردازشی کارت گرافیک را کاملاً درگیر و اشباع می‌کند. این مرحله «محدود به توان محاسباتی» است؛ یعنی ماهیت کار اساساً محاسبات سنگینی می‌طلبد و بهره‌وری هسته‌ها در آن بسیار بالا است — برای نمونه، در سناریوی مرجع این میزان به نزدیک ۹۵ درصد می‌رسد.

کدگشایی: محدود به پهنای باند حافظه

در مرحلهٔ decode، مدل در هر بار تنها یک توکن تولید می‌کند. در این حالت، ورودی به برداری کوچک کاهش می‌یابد و عملیات ضرب ماتریسی بسیار ناچیز می‌شود. اما در عوض، هزینه اصلی در جای دیگری رقم می‌خورد: در هر گام، کل حافظه نهان باید از حافظه اصلی خوانده شود. این مرحله «محدود به پهنای باند حافظه» است؛ به این معنا که در هر گام چندین گیگابایت داده جابه‌جا می‌شود بدون اینکه محاسبات چندانی روی آن‌ها صورت گیرد. برای نمونه، در سناریوی مرجع، بهره‌وری هسته‌ها در این مرحله به حدود ۱۰ درصد افت می‌کند، در حالی که پهنای باند حافظه به آستانهٔ اشباع می‌رسد.

چرا به حافظه نهان نیاز داریم

اگر هیچ سازوکار ذخیره‌سازی وجود نداشته باشد، کل توکن‌های گذشته باید در هر گام دوباره محاسبه شوند که فرآیندی بسیار ناکارآمد و هدردهنده منابع است. نکته کلیدی اینجاست: با اضافه شدن توکن جدید، مقادیر مورد نیاز از توکن‌های گذشته برای محاسبهٔ توجه تغییری نمی‌کنند، چرا که گذشته به آینده وابسته نیست. بنابراین، کافی است این مقادیر را تنها یک بار محاسبه و ذخیره کنیم تا نیازی به بازسازی مجدد آن‌ها در گام‌های بعدی نباشد. این سازوکار همان حافظه نهان کلید-مقدار (KV cache) است.

البته استفاده از این حافظه نهان بی‌هزینه نیست و حجم آن با طول متن به صورت خطی افزایش می‌یابد. برای نمونه، در مدل مرجع با ۳۲ لایه و بعد پنهان ۴۰۹۶ در دقت نیمه، هر توکن به حدود ۵۱۲ کیلوبایت حافظه نهان نیاز دارد؛ یعنی برای متنی با طول ۴۰۹۶ توکن، حدود ۲ گیگابایت حافظه مصرف می‌شود، آن هم تنها برای پاسخگویی به یک درخواست. به همین دلیل، اغلب ظرفیت همین حافظه نهان است که توانایی سامانه را در پاسخگویی هم‌زمان به کاربران تعیین می‌کند، نه ابعاد خود مدل.

پیامدهای عملی

در یک درخواست معمولی، مرحلهٔ کدگشایی بخش عمده‌ای از زمان را به خود اختصاص می‌دهد، چرا که تولید هر توکن یک گام مستقل به شمار می‌رود. از آنجا که این مرحله با محدودیت حافظه روبه‌رو است و نه توان محاسباتی، کارآمدترین راهکار برای افزایش توان عملیاتی، دسته‌بندی هم‌زمان چندین درخواست با یکدیگر است؛ با این کار، هزینهٔ خواندن وزن‌های مدل و بازیابی حافظه میان درخواست‌های مختلف سرشکن شده و توان کل سامانه به مراتب افزایش می‌یابد.