Ollama: ضبط الأداء

ضبط الأداء هو تغيير السرعات لمحرك الذكاء الاصطناعي المحلي الخاص بك — من الزحف بالسرعة الأولى إلى الطيران بالسرعة الخامسة.

💡 نصيحة: OLLAMA_NUM_PARALLEL يتحكم في عدد الطلبات المتوازية لكل نموذج — تعيينه إلى 4 يسمح بمعالجة 4 طلبات في وقت واحد، مما يزيد الإنتاجية. لـ 8 جيجابايت VRAM، ابدأ الاختبار من 2-4؛ لـ 16 جيجابايت، جرب 4-8. اجمع مع OLLAMA_KEEP_ALIVE=30m لتجنب التفريغ وإعادة التحميل المتكررين للنموذج.

📋 المتطلبات المسبقة: يجب أن تتقن أولاً ما يلي

1. ما ستتعلمه


2. قصة حقيقية من رائدة أعمال SaaS

💡 نصيحة: OLLAMA_KEEP_ALIVE يحدد المدة التي يبقى فيها النموذج في الذاكرة. لسيناريوهات التزامن العالي، عيّنه إلى 30m أو أطول لتجنب تأخيرات البدء البارد الناتجة عن التفريغ/إعادة التحميل المتكررين للنموذج (5-30 ثانية). للاستخدام غير المتكرر، عيّن 5m لتحرير VRAM.

ℹ️ معلومة: num_ctx (حجم نافذة السياق) يؤثر مباشرة على استخدام ذاكرة KV Cache. نموذج 8B مع num_ctx=2048 يحتاج حوالي 4 جيجابايت VRAM، وnum_ctx=8192 يحتاج حوالي 6 جيجابايت، وnum_ctx=32768 يحتاج حوالي 12 جيجابايت. عيّنه بناءً على الاحتياجات الفعلية، لا تزيده عميًا.

(1) المشكلة: انتهاء المهلة تحت التزامن العالي

بدأ SupportBot الخاص بـ Alice، وخلال ساعات الذروة، تسبب 10 طلبات متزامنة في ارتفاع زمن الاستجابة من ثانيتين إلى 30 ثانية. اشتكى العملاء من "الانتظار الطويل"، لكن Ollama يعالج طلبًا متزامنًا واحدًا فقط افتراضيًا.

(2) الحل: تحسين التزامن والسياق

ضبط OLLAMA_NUM_PARALLEL وOLLAMA_MAX_LOADED_MODELS، تعافى زمن الاستجابة إلى أقل من 5 ثوانٍ:

BASH
# Enable 4 parallel requests
export OLLAMA_NUM_PARALLEL=4
export OLLAMA_MAX_LOADED_MODELS=2

3. مقاييس الأداء الرئيسية

⚠️ تحذير: زيادة OLLAMA_NUM_PARALLEL لا تزيد الإنتاجية خطيًا — كل طلب متزامن يشارك GPU VRAM، والكثير من الطلبات المتزامنة قد يسبب OOM أو انخفاضًا حادًا في سرعة كل طلب. ابدأ من 2 واختبر تدريجيًا، مع مراقبة استخدام VRAM وتغيرات زمن الانتقال.

(1) ثلاثة مقاييس أساسية

المقياس الاسم الكامل المعنى القيمة المستهدفة
رموز/ثانية رموز في الثانية سرعة التوليد GPU: 30+، وحدة المعالجة المركزية: 5+
TTFT الوقت حتى أول رمز زمن انتقال الرمز الأول < 500 مللي ثانية (GPU)
الذاكرة استخدام VRAM/RAM استهلاك الموارد < 90% من السعة

(2) إحصائيات أداء Ollama

بيانات الأداء المضمنة في وضع --verbose أو الاستجابات غير المتدفقة:

الحقل المعنى
total_duration الوقت الإجمالي (نانوثانية)
load_duration وقت تحميل النموذج
prompt_eval_count عدد رموز الإدخال
prompt_eval_duration وقت معالجة الإدخال
eval_count عدد رموز الإخراج
eval_duration وقت توليد الإخراج

(1) ▶ مثال: جمع مقاييس الأداء

BASH
# Collect performance metrics with verbose output
ollama run --verbose qwen2.5 "Explain AI in 50 words"

# Key output:
# total duration:       2500000000    # 2.5s total
# load duration:        500000000     # 0.5s model load
# prompt eval count:    15 token(s)
# prompt eval duration: 200000000     # 0.2s input processing
# prompt eval count:    15 token(s)   # = prompt_eval_speed: 75 tok/s
# eval count:           52 token(s)
# eval duration:        1800000000    # = eval_speed: 28.9 tok/s

الإخراج:

TEXT
I'm a helpful AI assistant running locally on your machine...
PYTHON
import ollama
import time

def measure_performance(model: str, prompt: str) -> dict:
    start = time.time()
    response = ollama.chat(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        stream=False
    )
    total_time = time.time() - start
    content = response["message"]["content"]
    token_count = len(content) // 4  # Rough estimate

    return {
        "model": model,
        "total_time_s": round(total_time, 2),
        "estimated_tokens": token_count,
        "estimated_tok_per_s": round(token_count / total_time, 1) if total_time > 0 else 0
    }

result = measure_performance("qwen2.5", "Explain machine learning in 100 words")
print(result)

4. ضبط الطلبات المتزامنة

⚠️ ملاحظة: التزامن المفرط قد يسبب OOM — OLLAMA_NUM_PARALLEL لا يزيد الإنتاجية خطيًا؛ كل طلب متزامن يشارك GPU VRAM، والكثير من الطلبات المتزامنة يسبب انخفاضًا حادًا في سرعة كل طلب أو حتى تجاوز الذاكرة. ابدأ من 2 واختبر تدريجيًا، مع مراقبة استخدام VRAM وتغيرات زمن الانتقال.

(1) متغيرات البيئة المتعلقة بالتزامن

المتغير الافتراضي الوصف التأثير
OLLAMA_NUM_PARALLEL 1 الطلبات المتوازية لكل نموذج ↑الإنتاجية ↓سرعة كل طلب
OLLAMA_MAX_LOADED_MODELS 1 الحد الأقصى للنماذج المحملة في وقت واحد ↑تزامن النماذج المتعددة ↑VRAM
OLLAMA_KEEP_ALIVE 5m مدة بقاء النموذج في الذاكرة ↑يتجنب إعادة التحميل ↓استعادة الذاكرة

(2) قرار إعدادات التزامن

100%
flowchart LR
    A[طلبات متزامنة؟] --> B{ذروة QPS؟}
    B -->|1-2| C[افتراضي: NUM_PARALLEL=1]
    B -->|3-5| D[NUM_PARALLEL=4<br/>8 جيجابايت VRAM كحد أدنى]
    B -->|6-10| E[NUM_PARALLEL=8<br/>16 جيجابايت+ VRAM]
    B -->|10+| F[عقد متعددة<br/>موازن الحمل]
الإعدادات متطلب VRAM الإنتاجية زمن انتقال كل طلب
NUM_PARALLEL=1 الأدنى 1 طلب/ثانية الأقصر
NUM_PARALLEL=4 متوسط 3-4 طلب/ثانية زيادة طفيفة
NUM_PARALLEL=8 مرتفع 6-8 طلب/ثانية زيادة ملحوظة

(2) ▶ مثال: إعدادات التزامن والاختبار

BASH
# Configure parallel processing
sudo systemctl edit ollama
# Add:
[Service]
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_MAX_LOADED_MODELS=2"
Environment="OLLAMA_KEEP_ALIVE=30m"

sudo systemctl daemon-reload
sudo systemctl restart ollama

الإخراج:

TEXT
# Ollama command executed successfully
PYTHON
import ollama
import asyncio
import time

async def concurrent_test(num_requests: int):
    """Test concurrent request handling."""
    client = ollama.AsyncClient()
    start = time.time()

    async def single_request(i: int):
        req_start = time.time()
        response = await client.chat(
            model="qwen2.5",
            messages=[{"role": "user", "content": f"Say hello {i}"}],
            stream=False,
            options={"temperature": 0.1}
        )
        return time.time() - req_start

    tasks = [single_request(i) for i in range(num_requests)]
    latencies = await asyncio.gather(*tasks)

    total = time.time() - start
    print(f"Concurrent: {num_requests} requests")
    print(f"Total time: {total:.2f}s")
    print(f"Avg latency: {sum(latencies)/len(latencies):.2f}s")
    print(f"Throughput: {num_requests/total:.1f} req/s")

asyncio.run(concurrent_test(4))

5. إدارة نافذة السياق

(1) تأثير num_ctx على الذاكرة

num_ctx KV Cache (8B Q4_M) إجمالي VRAM المطلوب حالة الاستخدام
2048 ~1 جيجابايت ~6 جيجابايت محادثات قصيرة (افتراضي)
4096 ~2 جيجابايت ~7 جيجابايت محادثات متوسطة
8192 ~4 جيجابايت ~9 جيجابايت استرجاع RAG
32768 ~16 جيجابايت ~21 جيجابايت مستندات طويلة
⚠️ ملاحظة: KV Cache ينمو خطيًا مع num_ctx. نموذج 8B مع num_ctx=32768 لا يمكن تشغيله على 8 جيجابايت VRAM؛ يحتاج 21 جيجابايت+ VRAM.

(2) استراتيجيات تحسين num_ctx

الاستراتيجية الطريقة التأثير
تعيين حسب الطلب المحادثة تستخدم 2048، RAG يستخدم 8192 تقليل هدر الذاكرة
نافذة منزلقة الاحتفاظ بآخر N جولات محادثة فقط التحكم في طول الإدخال
ضغط الملخص ضغط المحادثات المبكرة دوريًا توفير مساحة السياق
تنقية RAG تقليل top-k من 5 إلى 3 تقليل رموز الإدخال

(3) ▶ مثال: اختبار مقارنة num_ctx

PYTHON
import ollama
import time

def test_context_sizes(model: str, prompt: str, context_sizes: list[int]):
    """Test performance with different context window sizes."""
    for ctx in context_sizes:
        start = time.time()
        response = ollama.chat(
            model=model,
            messages=[{"role": "user", "content": prompt}],
            stream=False,
            options={"num_ctx": ctx, "temperature": 0.3}
        )
        elapsed = time.time() - start
        tokens = len(response["message"]["content"]) // 4
        speed = tokens / elapsed if elapsed > 0 else 0
        print(f"num_ctx={ctx}: {elapsed:.2f}s, ~{speed:.1f} tok/s")

test_context_sizes("qwen2.5", "Explain AI briefly", [2048, 4096, 8192])

الإخراج:

TEXT
# Function defined successfully

6. المعالجة الدفعية والقياس

(1) استراتيجيات المعالجة الدفعية

الاستراتيجية الوصف حالة الاستخدام
دفعة تسلسلية معالجة الطلبات واحدًا تلو الآخر بسيطة وموثوقة
دفعة متوازية تزامن asyncio إنتاجية عالية
جدولة الصفوف FastAPI + صف بيئة الإنتاج

(4) ▶ مثال: نص قياس دفعي

PYTHON
import ollama
import time
import asyncio
import json
from datetime import datetime

class BenchmarkRunner:
    def __init__(self, model: str = "qwen2.5"):
        self.model = model
        self.results = []

    def warmup(self, runs: int = 2):
        for _ in range(runs):
            ollama.chat(model=self.model,
                        messages=[{"role": "user", "content": "warmup"}],
                        stream=False)

    def single_benchmark(self, prompt: str) -> dict:
        start = time.time()
        response = ollama.chat(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            stream=False,
            options={"temperature": 0.3}
        )
        elapsed = time.time() - start
        content = response["message"]["content"]
        return {
            "prompt_length": len(prompt),
            "response_length": len(content),
            "estimated_tokens": len(content) // 4,
            "total_time_s": round(elapsed, 2),
            "tok_per_s": round(len(content) / 4 / elapsed, 1) if elapsed > 0 else 0
        }

    async def concurrent_benchmark(self, prompt: str, concurrency: int) -> dict:
        client = ollama.AsyncClient()
        start = time.time()
        tasks = [
            client.chat(model=self.model,
                       messages=[{"role": "user", "content": prompt}],
                       stream=False, options={"temperature": 0.3})
            for _ in range(concurrency)
        ]
        await asyncio.gather(*tasks)
        total = time.time() - start
        return {
            "concurrency": concurrency,
            "total_time_s": round(total, 2),
            "throughput_rps": round(concurrency / total, 1)
        }

    def run_full_benchmark(self):
        self.warmup()
        prompts = [
            "Hello",
            "Explain AI in 3 sentences",
            "Write a product description for wireless headphones"
        ]
        print("=== Single Request Benchmark ===")
        for p in prompts:
            result = self.single_benchmark(p)
            print(f"  {result['estimated_tokens']}tok, {result['tok_per_s']}tok/s, {result['total_time_s']}s")

        print("\n=== Concurrent Benchmark ===")
        for c in [1, 2, 4]:
            result = asyncio.run(self.concurrent_benchmark("Hello", c))
            print(f"  Concurrency {c}: {result['throughput_rps']} req/s")

# Run
runner = BenchmarkRunner("qwen2.5")
runner.run_full_benchmark()

الإخراج:

TEXT
=== Single Request Benchmark ===

=== Concurrent Benchmark ===

(5) ▶ مثال: تأسيس خط أساس الأداء

BASH
#!/bin/bash
# Establish performance baseline

MODEL="qwen2.5"
DATE=$(date +%Y%m%d)
REPORT="baseline_${DATE}.txt"

echo "=== Performance Baseline ===" > "$REPORT"
echo "Date: $(date)" >> "$REPORT"
echo "Model: $MODEL" >> "$REPORT"
echo "GPU: $(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || echo 'CPU')" >> "$REPORT"
echo "" >> "$REPORT"

# Single request benchmark
echo "## Single Request ##" >> "$REPORT"
for i in {1..5}; do
    start=$(date +%s%N)
    curl -s http://localhost:11434/api/chat -d "{
        \"model\": \"$MODEL\",
        \"messages\": [{\"role\": \"user\", \"content\": \"Hello\"}],
        \"stream\": false
    }" > /dev/null
    end=$(date +%s%N)
    elapsed=$(( (end - start) / 1000000 ))
    echo "  Run $i: ${elapsed}ms" >> "$REPORT"
done

echo "" >> "$REPORT"
echo "## GPU Utilization ##" >> "$REPORT"
nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv >> "$REPORT" 2>/dev/null || echo "CPU mode" >> "$REPORT"

echo "Baseline saved: $REPORT"

الإخراج:

TEXT
{"status":"ok","data":{}}

7. مثال شامل: تقرير ضبط أداء SupportBot

PYTHON
# ============================================
# Comprehensive: Performance tuning report
# Full benchmark + optimization recommendations
# ============================================

import ollama
import time
import asyncio
import json

class PerformanceTuner:
    def __init__(self, model: str = "qwen2.5"):
        self.model = model

    def run_diagnostics(self) -> dict:
        """Run full performance diagnostics."""
        # Warm up
        ollama.chat(model=self.model,
                    messages=[{"role": "user", "content": "warmup"}],
                    stream=False)

        # Single request test
        start = time.time()
        resp = ollama.chat(
            model=self.model,
            messages=[{"role": "user", "content": "Hello"}],
            stream=False, options={"temperature": 0.3}
        )
        single_latency = time.time() - start

        # Context size test
        ctx_results = {}
        for ctx in [2048, 4096, 8192]:
            start = time.time()
            ollama.chat(
                model=self.model,
                messages=[{"role": "user", "content": "Hello"}],
                stream=False, options={"num_ctx": ctx, "temperature": 0.3}
            )
            ctx_results[ctx] = round(time.time() - start, 2)

        return {
            "model": self.model,
            "single_latency_s": round(single_latency, 2),
            "context_sizes": ctx_results,
            "recommendations": self._generate_recommendations(single_latency)
        }

    def _generate_recommendations(self, latency: float) -> list[str]:
        recs = []
        if latency > 5:
            recs.append("High latency detected. Enable GPU acceleration or use smaller model.")
        if latency > 2:
            recs.append("Set OLLAMA_NUM_PARALLEL=4 for concurrent requests.")
        recs.append("Use num_ctx=2048 for chat, num_ctx=8192 for RAG.")
        recs.append("Set OLLAMA_KEEP_ALIVE=30m to avoid model reloading.")
        return recs

    def generate_report(self) -> str:
        diag = self.run_diagnostics()
        report = [
            f"# Performance Tuning Report",
            f"Model: {diag['model']}",
            f"Single request latency: {diag['single_latency_s']}s",
            f"\n## Context Window Impact:",
        ]
        for ctx, latency in diag["context_sizes"].items():
            report.append(f"  num_ctx={ctx}: {latency}s")
        report.append("\n## Recommendations:")
        for r in diag["recommendations"]:
            report.append(f"  - {r}")
        return "\n".join(report)

tuner = PerformanceTuner("qwen2.5")
print(tuner.generate_report())

❓ أسئلة شائعة

س ماذا يجب تعيين OLLAMA_NUM_PARALLEL؟
ج 2-4 لـ 8 جيجابايت VRAM، 4-8 لـ 16 جيجابايت، 8 لـ 24 جيجابايت. الكثير من الطلبات المتوازية يزيد زمن انتقال كل طلب ويخاطر بـ OOM. ابدأ الاختبار من 4.
س ماذا لو كان TTFT مرتفعًا جدًا؟
ج 1) تأكد أن OLLAMA_KEEP_ALIVE طويل بما يكفي (تجنب البدء البارد)؛ 2) قلل num_ctx؛ 3) استخدم نموذجًا أصغر؛ 4) تأكد أن تسريع GPU يعمل.
س كيف أقلل وقت تحميل النموذج؟
ج عيّن OLLAMA_KEEP_ALIVE=30m أو أطول لإبقاء النموذج مقيمًا في الذاكرة. التحميل الأول لا مفر منه؛ الطلبات اللاحقة تستجيب في ثوانٍ.
س ماذا لو حدث OOM أثناء الطلبات المتزامنة؟
ج قلل OLLAMA_NUM_PARALLEL؛ استخدم num_ctx أصغر؛ قلل OLLAMA_MAX_LOADED_MODELS؛ أو أضف المزيد من VRAM.
س ماذا لو كانت نتائج القياس غير مستقرة؟
ج 1) إحماء 2-3 مرات للقضاء على البدء البارد؛ 2) أغلق برامج GPU الأخرى؛ 3) خذ 5+ مرات ووسطها؛ 4) ثبت البذرة العشوائية.
س كيف أراقب أداء بيئة الإنتاج؟
ج الدرس 21 سيتناول بالتفصيل مكدس مراقبة Prometheus + Grafana. حاليًا، استخدم --verbose ونصوصًا مخصصة لجمع المقاييس.

📖 ملخص


📝 تمارين

  1. أساسي (⭐): شغّل وضع --verbose، سجّل مقاييس رموز/ثانية وTTFT وغيرها لـ 3 مرات استنتاج، وأسس خط أساس الأداء الخاص بك.
  2. متوسط (⭐⭐): إعدادات OLLAMA_NUM_PARALLEL=4، شغّل اختبارات متزامنة، وقارن تغيرات الإنتاجية وزمن الانتقال قبل وبعد التزامن.
  3. متقدم (⭐⭐⭐): اكتب تقرير ضبط أداء كامل — يتضمن قياسات الطلبات الفردية، اختبارات متزامنة، مقارنة num_ctx، وتوصيات التحسين — بتنسيق Markdown.
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%