مسار packed Q8 الجديد رفع median للـ full-model decode من 21.968 إلى 30.888 tok/s في تشغيل Llama-3.2-1B المقاس هنا. التحسن 40.6%. لكن النسخة الأولى بدت أيضاً كأنها تحتاج نسخة إضافية بحجم نموذج تقريباً داخل الذاكرة. رقم السرعة كان حقيقياً. قراءة الذاكرة الأولى كانت ناقصة.
السؤال صار أضيق: هل أقدر أحتفظ بالـ layout المناسب للـ decode، ثم أخرج صفحات source projections بعد prefill، بدون ما يتغير الخرج؟ في هذا النموذج وعلى هذا المعالج، نعم. هذا دليل هندسي محلي، مو claim عامة عن Q8 inference.
| النتيجة المقاسة | 21.968 → 30.888 tok/s في median full-model decode، بزيادة 40.6%. |
| أفضل اختيار جزئي | 30.199 tok/s بدون down projections؛ احتفظ بـ 92.9% من التحسن الكامل المقاس. |
| كلفة البداية | packing لكل projections أخذ قرابة 450 ms؛ الاختيار الجزئي أخذ 350 ms. |
| حد الادعاء | نموذج Llama-3.2-1B Q8_0 واحد، لابتوب Intel بأربع cores، وثلاث إعادات بترتيب عشوائي. |
التحديث السابق خفف الشغل حول matrix kernels. single-token decode صار يعيد استخدام workspace محاذي للـ cache بدل بناء tensors وسيطة في كل طبقة. KV cache صار FP16. مسارات Q/K/V وgate/up تشارك activation quantization واحدة. والـ LM head الكبير أخذ interleaved VNNI layout خاص. كذلك kernel الصفوف العادي توسع من 4 إلى 8 output rows وأضاف prefetch صريح.
التحديث الأخير يغير layout الأوزان نفسها. Llama projections المؤهلة تنحزم في tiles من 16 output rows. لكل block من 32 input coordinates، بايتات نفس الأربع coordinates عبر الصفوف الستة عشر تجلس بجانب بعض. بعدها يقدر AVX-512 VNNI يبث أربع activation bytes، يقرأ weight group واحد بحجم 64 bytes، ويجمع 16 outputs.
row-contiguous source
row 0 [scale][q0 q1 ... q31]
row 1 [scale][q0 q1 ... q31]
...
packed 16-row tile
[rows 0..15, q0..q3]
[rows 0..15, q4..q7]
...
[rows 0..15, q28..q31]
[16 fp16 scales]
كل packed tile/block حجمه 544 bytes، وهو نفس الحجم المشفر لـ 16 Q8_0 blocks عادية. packing ما يضغط الوزن أكثر؛ فقط يغير أي بايتات تصل مع بعض. المعالجات غير المدعومة، ومسار Qwen ذي split-half RoPE، والـ projections التي فيها bias، ومفتاح الرجوع EMBER_LLAMA_PACKED_Q8=0 تبقى على المسار العام.
كل trial اشتغل في process جديد على Intel i5-1135G7. ثبتت أربعة Rayon workers على physical CPUs من 0 إلى 3 واستبعدت SMT siblings. حرارة package عند البداية كانت 80 °C أو أقل. prompt من ستة tokens مر عبر generic prefill، ثم النموذج ولد 128 greedy tokens بحتمية. أوضاع lifecycle واختيارات projections العشرة تغير ترتيبها ببذرة ثابتة عبر ثلاث إعادات.
كل الـ 30 trials المقبولة أعطت نفس hash للـ generated tokens: fnv1a64:9f8e8158645ba677. تشغيل أقدم جمع الأوضاع بدل خلطها، ووضع إعدادين متطابقين على فرق 13%. رفضت أرقامه. في التشغيل العشوائي صار الفرق بين نفس الإعدادين 0.2%.
packing لكل شيء وصل إلى 30.827 tok/s داخل selection matrix. لما حزمت gate/up وQ/K/V/O وتركت down على layout الصفوف، وصلت النتيجة إلى 30.199 tok/s. الاختيار الأصغر حجمه 714 MiB واحتفظ بـ 92.9% من التحسن، مع خفض packing time من 452.9 إلى 350.3 ms وخفض transient peak RSS من 3,104.5 إلى 2,827.4 MiB.
down هو negative result المفيد. إضافته فوق gate/up حجزت 272 MiB أخرى، لكن median ارتفع فقط 0.393 tok/s. operator profile يعطي نفس الصورة المحلية: Q وO وgate وup تحسنت تقريباً 1.7–1.8x داخل projection calls، بينما down كان 1.03x والـ LM head المنفصل 1.01x. هذا لا يفسر السبب. ما زلت أحتاج hardware counters وحجم نموذج ثانٍ.
GGUF loader يبقي أوزان Q8_0 الأصلية row-contiguous داخل read-only mmap. الأوزان packed تذهب إلى anonymous memory. بعد packing مباشرة يقدر Ember يستدعي MADV_DONTNEED على source ranges. في decode-only run، هذا يبدو مثل استبدال layout بآخر.
generation العادي يبدأ بـ prefill. generic prefill يقرأ source layout ويرجع صفحاته إلى الذاكرة. إذا ما صار eviction ثانية عند حد الانتقال، يبقى layoutان resident. لذلك نتيجة decode-only قليلة الزيادة في الذاكرة ما كانت تصف دورة التشغيل العادية.
في durable all-projection mode هبط file-backed PSS من 1,265.1 إلى 279.0 MiB بعد re-eviction، وبقي هناك خلال 127 packed decode evaluations. final RSS كان 2,113.6 MiB. أما duplicate-layout mode المقصود للمقارنة فانتهى عند 3,099.7 MiB بدون decode أسرع. هذه هي النتيجة التي تدعم replacement-style residency.
decode الأسرع في steady state لا يعني أن generation القصير يصير أسرع. all-projection mode أضاف قرابة 450 ms من packing إلى time-to-first-token، ووصل break-even عند قرابة 36 generated tokens لهذا prompt. استبعاد down خفض النقطة المقاسة إلى 29 tokens.
ممكن نؤجل packing إلى ما بعد prefill ونخرج token الأول قبل الوقفة. لكن هذا يصنع فجوة تقارب 438 ms بين التوكن الأول والثاني. نقلنا latency من مكان إلى آخر؛ ما أزلناها. السياسة المناسبة تعتمد على عمر process وعدد tokens المتوقع.
الادعاء الأضيق يكفي الآن: packed kernel سرّع decode، وحد المرحلة جعل السرعة متوافقة مع durable source-page eviction في هذا workload. الاختبار التالي هو Llama-3.2-3B، قبل تعديل kernel مرة ثانية.