تشغيل كرتين MI50 16GB مع vLLM: المشكلة والحل
مقدمة
في هذا المقال بتكلم عن مشكلة واجهتني يوم حاولت أشغّل كرتين MI50 16GB مع بعض، وكيف حلّيتها بمذربورد جديدة، وليه أستخدم vLLM بدل llama.cpp، والنتائج اللي طلعت معي.
العتاد والبيئة
| المكوّن | المواصفات |
|---|---|
| المذربورد | X99-F8D Plus (شيبست Intel C610/X99) |
| المعالج | Intel Xeon E5-2680 v4 @ 2.40GHz (14 نواة / 28 مسار، معالج واحد) |
| الرامات | 32GB DDR4 |
| الكروت | 2× AMD Radeon Instinct MI50 (gfx906 / Vega 20، 16GB لكل كرت) |
| كرت العرض | NVIDIA GeForce 210 (للعرض فقط) |
| النظام | Proxmox VE (كيرنل 6.8.12-16-pve) + LXC (Debian 13، 10 أنوية) |
| ROCm | 7.14 (libamdhip64 7.14.60850) |
| PyTorch | 2.13.0+gfx906 (build مخصص لـ gfx906) |
| vLLM | 0.30.0، image: vllm-gfx906-kintegrated:0.30.0-rocm7.14-opt |
ملاحظة: الكروت تظهر في النظام باسم “Radeon Pro VII” (PCI ID 0x66a1). هي نفس شريحة MI50 لكن بفيرموير Pro VII.
المشكلة
المشكلة كانت إن الكرت الأول له اتصال مباشر بالمعالج، والكرت الثاني كان يتصل عن طريق شيبست المذربورد ثم المعالج. وحسب تشخيصي، هذا اللي سبّب لي مشكلة متعلقة بـ RCCL.
لأني إذا جيت أستضيف نموذج ذكاء اصطناعي، خصوصًا على vLLM، ما كان يقبل يقسّم النموذج على الكرتين. يعني عندك كرتين وما تقدر تستفيد إلا من واحد.
المذربورد القديمة كانت [اسم المذربورد القديمة].
الحل
لين صمّلت وشريت مذربورد جديدة مخصصة للسيرفرات، وهي X99-F8D Plus.
أفضل شي فيها إنها تدعم رامات DDR4 العادية (Desktop) وما تشترط رامات سيرفرات. والأفضل بعد إنها تدعم رامات السيرفرات لو بغيت، وهذا شي إيجابي جدًا.
وأيضًا تدعم معالجين (Dual CPU)، وأنا حاليًا مركّب معالج واحد: Xeon E5-2680 v4 بـ 14 نواة و28 مسار.
من ناحية البايوس، فعّلت Above 4G Decoding.
وهذا شكل التوصيل بعد التغيير. الكرتين موصلين بالمعالج مباشرة (CPU root complex)، مو عن طريق الشيبست:
-+-[0000:00]-+-02.2-[04-06]--00.0-[05-06]--00.0-[06]--00.0 AMD Vega 20 [MI50]
| +-03.0-[07-09]--00.0-[08-09]--00.0-[09]--00.0 AMD Vega 20 [MI50]
| +-11.0 Intel C610/X99 chipset SPSR
إعداد LXC و vLLM
النظام الأساسي Proxmox، و vLLM شغال داخل Docker على حاوية LXC اسمها GFX، والكرتين ممرّرين لها (passthrough).
وهذا أمر التشغيل بالإعداد النهائي (مع MTP):
docker run --rm --name vllm-qwen38 \
--device=/dev/kfd --device=/dev/dri --group-add 992 --group-add 44 \
--ipc host --shm-size 16g -p 8000:8000 \
-v /root/gfx-build/models/Qwen3.8-27B-AWQ-INT4:/models/Qwen3.8-27B-AWQ-INT4:ro \
-v /root/gfx-build/cache:/root/.cache \
-e HIP_VISIBLE_DEVICES=0,1 \
-e FLASH_ATTENTION_TRITON_AMD_ENABLE=TRUE \
-e VLLM_GFX906_HIP_LIB_PATH=/opt/rocm/lib/libamdhip64.so.7 \
-e VLLM_USE_V2_MODEL_RUNNER=0 \
vllm-gfx906-kintegrated:0.30.0-rocm7.14-opt \
vllm serve /models/Qwen3.8-27B-AWQ-INT4 \
--served-model-name qwen3.8-27b \
--host 0.0.0.0 --port 8000 \
--tensor-parallel-size 2 \
--dtype float16 \
--max-model-len 204800 \
--max-num-seqs 1 \
--max-num-batched-tokens 2048 \
--gpu-memory-utilization 0.92 \
--language-model-only \
--compilation-config '{"cudagraph_capture_sizes":[4]}' \
--kv-cache-dtype int8_per_token_head \
--speculative-config '{"method":"mtp","num_speculative_tokens":3}' \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coder \
--reasoning-parser qwen3 \
--generation-config auto \
--kv-cache-memory-bytes 4402341478
النموذج Qwen3.8-27B-AWQ-INT4 مقسوم على الكرتين بـ tensor-parallel-size 2، والـ KV cache بصيغة int8_per_token_head، والـ context يوصل إلى 204,800 توكن.
حاط على الكروت حد طاقة 90 واط لكل كرت (الافتراضي 190 واط). والـ RCCL بين الكرتين ما فيه P2P، فالبيانات تمر عن طريق الذاكرة المشتركة (SHM) في الهوست، بحدود 75 ميكروثانية لكل all-reduce.
النتائج
أول تجربة كانت على نموذج Qwen3.8 27B، والنتيجة كانت مبهرة: 21 توكن بالثانية، وهذا بدون MTP ولا DFlash2.
وبعد ما فعّلتهم زادت السرعة أكثر من الضعف:
| الإعداد | prompt قصير | context 1.5k | context 4.6k | context 14.6k |
|---|---|---|---|---|
| MTP (k=3) | 49.9 | 45.7 | — | — |
| MTP + kernels مخصصة | 65.1 | 59.6 | 59.1 | 48.2 |
| DFlash2 (k=7) | 74–81 | 64–69 | 53.8 | 44.4 |
الأرقام بالتوكن في الثانية، وتنزل كل ما طال الـ context.
السرعة على context طويل (int8 KV + MTP)
| Context | السرعة |
|---|---|
| قصير | 70 توكن/ثانية |
| ~63k | 29–32 توكن/ثانية |
| ~143k | 17.6 توكن/ثانية |
ليه vLLM بدل llama.cpp
عندي عدة أسباب، وأهمها إن vLLM يتعامل مع الـ KV cache بشكل ذكي جدًا، وهذا يسمح لي أستخدم النموذج أكثر من مرة بنفس الوقت.
يعني لو عندي أكثر من إيجنت، أقدر أشغّلهم كلهم على نفس النموذج.
أنا أستخدم Hermes Agent بشكل يومي، ولجلسات طويلة وعميقة. فلو احتجت أكلّم النموذج كشات عادي وأتناقش معه، أقدر بكل سهولة وبدون ما أوقّف شغل Hermes Agent.
عكس llama.cpp: لو كلّمته بشات عادي يوقف شغل Hermes Agent ويعيد كل شي. وإذا خلصت يبدأ يسوي prompt processing لـ Hermes Agent من جديد، ولو كان الـ context 160 ألف توكن فهذا ضخم جدًا وبطيء جدًا.
ولو كان Hermes Agent شغال وجيت أستخدم شات خارجي، ما يرد عليّ إلا لين يخلص Hermes Agent شغله. وهذا كان مضايقني جدًا.
وأيضًا أنا دايم أبني إيجنتات وأبي أجربها، لكن يوم كنت على llama.cpp كنت محدود وما أقدر.
مع vLLM الوضع اختلف تمامًا: صرت أقدر أستخدم النموذج أكثر من مرة، من أكثر من مكان، وبنفس الوقت. وهذي الميزة بالنسبة لي جوهرية، وتستاهل إني أدفع أكثر عشانها.
ليه أستضيف النماذج محليًا
أكيد السبب هو الامتلاك والخصوصية. وأيضًا أقدر أجرب على راحتي كل ما تطورت النماذج، وأقدر أدرّب نماذج لو بغيت.
الخلاصة
- المشكلة، حسب تشخيصي، كانت في المذربورد نفسها، مو في البرامج ولا في النموذج.
- مذربورد سيرفرات (X99-F8D Plus) حلّت المشكلة، وتدعم الرامات العادية ورامات السيرفرات.
- الفرق بين vLLM و llama.cpp جوهري، خصوصًا لو بتستخدم النموذج من أكثر من مكان بنفس الوقت.
ولا زلت أضبط البيئة، وبكتب لكم عن التجارب الجاية إن شاء الله.