الدرس 18 من 22

الأداء والتحسين

ضبط الأداء

3 دقيقة للقراءة

حسّن بارامترات Ollama لتعظيم سرعة الاستدلال لعتادك وحالة استخدامك المحددة.

بارامترات الأداء الرئيسية

┌─────────────────────────────────────────────────────────────────┐
│                   بارامترات أداء Ollama                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  البارامتر         │ التأثير             │ المقايضة             │
│  ──────────────────│─────────────────────│───────────────────   │
│  num_ctx           │ حجم نافذة السياق    │ السرعة مقابل الذاكرة │
│  num_gpu           │ طبقات GPU           │ السرعة مقابل VRAM    │
│  num_thread        │ مسارات CPU          │ سرعة استدلال CPU     │
│  num_batch         │ حجم الدفعة          │ الإنتاجية مقابل التأخر│
│  num_predict       │ أقصى توكنات         │ طول الرد             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

تحسين طول السياق

طول السياق يؤثر بشكل كبير على استخدام الذاكرة والسرعة.

import ollama

# سياق افتراضي (2048 توكن) - الأسرع
response = ollama.generate(
    model="llama3.2",
    prompt="مرحباً!",
    options={"num_ctx": 2048}
)

# سياق ممتد (8192 توكن) - يستخدم VRAM أكثر
response = ollama.generate(
    model="llama3.2",
    prompt="مستند طويل هنا...",
    options={"num_ctx": 8192}
)

# أقصى سياق (131072 لبعض النماذج) - الأبطأ، أكثر ذاكرة
response = ollama.generate(
    model="llama3.2",
    prompt="مستند طويل جداً...",
    options={"num_ctx": 131072}
)

صيغة حساب الذاكرة:

VRAM_مطلوب ≈ حجم_النموذج + (num_ctx × الحجم_المخفي × عدد_الطبقات × 2 بايت)

تكوين طبقات GPU

تحكم في عدد الطبقات التي تعمل على GPU مقابل CPU.

# تحقق من استخدام GPU الحالي
ollama ps

# شغّل بكل الطبقات على GPU (إذا VRAM تسمح)
OLLAMA_NUM_GPU=999 ollama run llama3.2

# شغّل بتحميل جزئي على GPU (قسّم بين GPU و CPU)
OLLAMA_NUM_GPU=20 ollama run llama3.2:70b

# فرض CPU فقط
OLLAMA_NUM_GPU=0 ollama run llama3.2
import ollama

def test_gpu_layers(model: str, layer_counts: list[int]):
    """اختبر سرعة الاستدلال بأعداد مختلفة من طبقات GPU."""
    import time

    results = []
    prompt = "اكتب قصيدة قصيرة عن البرمجة."

    for layers in layer_counts:
        start = time.time()
        response = ollama.generate(
            model=model,
            prompt=prompt,
            options={"num_predict": 50}
        )
        elapsed = time.time() - start

        tokens = response.get("eval_count", 50)
        results.append({
            "gpu_layers": layers,
            "tokens_per_sec": tokens / elapsed
        })

    return results

ضبط حجم الدفعة

import ollama
import time

def benchmark_batch_sizes(model: str, batch_sizes: list[int]):
    """قارن الإنتاجية بأحجام دفعات مختلفة."""
    prompt = "اشرح تعلم الآلة."
    results = []

    for batch in batch_sizes:
        start = time.time()
        response = ollama.generate(
            model=model,
            prompt=prompt,
            options={
                "num_batch": batch,
                "num_predict": 100
            }
        )
        elapsed = time.time() - start

        results.append({
            "batch_size": batch,
            "time": elapsed,
            "tokens_per_sec": response.get("eval_count", 100) / elapsed
        })

    return results

# اختبر أحجام دفعات مختلفة
results = benchmark_batch_sizes("llama3.2", [128, 256, 512, 1024])
for r in results:
    print(f"دفعة {r['batch_size']}: {r['tokens_per_sec']:.1f} توكن/ثانية")

تكوين المسارات

# عيّن مسارات CPU (افتراضي: اكتشاف تلقائي)
OLLAMA_NUM_THREAD=8 ollama serve

# الأمثل: عادةً عدد_النوى_الفيزيائية (ليس hyperthreads)
# على CPU بـ 8 نوى مع hyperthreading، استخدم 8، ليس 16
import os
import multiprocessing

def get_optimal_threads():
    """احصل على عدد المسارات الموصى به للاستدلال."""
    # النوى الفيزيائية عادةً تعمل أفضل من المنطقية
    logical = multiprocessing.cpu_count()

    # قدّر النوى الفيزيائية (تقريب)
    # معظم CPUs المستهلك لديها 2 مسار لكل نواة
    physical = logical // 2

    return {
        "logical_cores": logical,
        "physical_cores": physical,
        "recommended": physical
    }

print(get_optimal_threads())

إبقاء النموذج محمّلاً

أبقِ النماذج محمّلة لتجنب تأخر إعادة التحميل.

import ollama

# أبقِ النموذج محمّلاً للأبد
response = ollama.generate(
    model="llama3.2",
    prompt="سؤال سريع",
    keep_alive=-1  # لا تفرّغ أبداً
)

# فرّغ بعد 5 دقائق من عدم النشاط (افتراضي)
response = ollama.generate(
    model="llama3.2",
    prompt="سؤال سريع",
    keep_alive="5m"
)

# فرّغ فوراً بعد الرد
response = ollama.generate(
    model="llama3.2",
    prompt="استعلام لمرة واحدة",
    keep_alive=0  # فرّغ فوراً
)
# عيّن وقت البقاء الافتراضي
OLLAMA_KEEP_ALIVE=1h ollama serve

سكربت قياس أداء شامل

import ollama
import time
import statistics

def full_benchmark(model: str, iterations: int = 5):
    """شغّل قياس أداء شامل."""
    prompts = [
        "ما هو 2+2؟",  # قصير
        "اشرح الحوسبة الكمية بالتفصيل.",  # متوسط
        "اكتب قصة عن روبوت يتعلم الرسم.",  # توليد طويل
    ]

    results = {
        "model": model,
        "prompt_processing": [],  # وقت لأول توكن
        "generation_speed": [],   # توكنات في الثانية
        "total_time": []
    }

    for prompt in prompts:
        for _ in range(iterations):
            start = time.time()

            response = ollama.generate(
                model=model,
                prompt=prompt,
                options={"num_predict": 100}
            )

            elapsed = time.time() - start

            # احسب المقاييس
            eval_count = response.get("eval_count", 100)
            prompt_eval_duration = response.get("prompt_eval_duration", 0) / 1e9
            eval_duration = response.get("eval_duration", elapsed * 1e9) / 1e9

            results["prompt_processing"].append(prompt_eval_duration)
            results["generation_speed"].append(eval_count / eval_duration if eval_duration > 0 else 0)
            results["total_time"].append(elapsed)

    # اجمع النتائج
    return {
        "model": model,
        "avg_prompt_processing_ms": statistics.mean(results["prompt_processing"]) * 1000,
        "avg_tokens_per_sec": statistics.mean(results["generation_speed"]),
        "avg_total_time_sec": statistics.mean(results["total_time"]),
        "iterations": iterations * len(prompts)
    }

# شغّل القياس
benchmark = full_benchmark("llama3.2")
print(f"النموذج: {benchmark['model']}")
print(f"معالجة الطلب: {benchmark['avg_prompt_processing_ms']:.1f}ms")
print(f"سرعة التوليد: {benchmark['avg_tokens_per_sec']:.1f} توكن/ثانية")
print(f"متوسط الوقت الكلي: {benchmark['avg_total_time_sec']:.2f}s")

ملخص متغيرات البيئة

# أنشئ تكوين Ollama محسّن
export OLLAMA_NUM_GPU=999        # استخدم كل طبقات GPU
export OLLAMA_NUM_THREAD=8       # طابق نوى CPU الفيزيائية
export OLLAMA_KEEP_ALIVE=1h      # أبقِ النماذج محمّلة
export OLLAMA_MAX_LOADED_MODELS=2 # اسمح بنموذجين في الذاكرة
export OLLAMA_FLASH_ATTENTION=1  # فعّل flash attention (أسرع)

# ابدأ Ollama بالإعدادات المحسّنة
ollama serve

ملخص نصائح الأداء

الهدف الإعداد
أسرع استدلال num_ctx أقل، كل طبقات GPU
سياق أطول زد num_ctx، قد تحتاج طبقات GPU أقل
مستخدمين متعددين زد num_batch، أبقِ النماذج محمّلة
توفير الذاكرة قلل num_ctx، استخدم ضغط Q4
ردود سريعة عيّن حد num_predict

ضبط الأداء تكراري. بعد ذلك، سنستكشف متى تنتقل من Ollama إلى vLLM لأحمال الإنتاج. :::

اختبار

الوحدة 5: الأداء والتحسين

خذ الاختبار