Nota

Imposta un semplice sistema di benchmarking inferenziale LLM con vLLM su Oracle Cloud Infrastructure Compute

Introduzione

Comprendere le caratteristiche prestazionali di un sistema ti consente di fare scelte informate sui suoi componenti e su cosa aspettarti da essi. In questa esercitazione verrà impostato un sistema di benchmarking completo per l'inferenza LLM (Large Language Model) AI. Utilizzando questo, puoi eseguire vari esperimenti, dalla valutazione dell'idoneità di una particolare forma di computazione Oracle Cloud Infrastructure (OCI) per l'LLM e i requisiti di performance in mente al confronto tra vari LLM.

In questa esercitazione viene utilizzato il popolare server di inferenza open source vLLM, che supporta molti LLM all'avanguardia e offre ottimizzazioni delle prestazioni cruciali per servire in modo efficiente questi modelli ed è in grado di gestire migliaia di richieste concorrenti. Inoltre, utilizziamo Ray LLMPerf per eseguire i benchmark.

Nota: in questa esercitazione si presuppone che si disponga di una tenancy OCI con una quota GPU. Per ulteriori informazioni sulle quote, vedere Calcola quote.

Obiettivi

Prerequisiti

Task 1: configurare la rete

In questo task, configurare la rete cloud virtuale (VCN) per fornire un'impostazione funzionale e sicura per eseguire i benchmark. In questo esempio, abbiamo bisogno di una VCN con una subnet pubblica e una privata. È possibile utilizzare la configurazione guidata della VCN o impostare manualmente tutti i componenti, assicurandosi che le liste di sicurezza consentano l'accesso SSH e che venga assegnato un gateway NAT alla subnet privata.

Panoramica dell'architettura della soluzione che comprende una subnet privata e pubblica un bastion host e la VM GPU in fase di test

Prima di eseguire il provisioning delle istanze di OCI Compute e di procedere con l'impostazione del benchmark, è importante assicurarsi che le impostazioni di rete siano configurate correttamente. Per impostazione predefinita, la rete di cui è stato eseguito il provisioning consente solo il traffico essenziale tra le subnet. Per consentire il traffico di rete necessario per il benchmarking, sarà necessario modificare queste impostazioni in modo da consentire il traffico HTTP tra le subnet pubbliche e private. In particolare, aggiungi una regola di entrata alla lista di sicurezza della subnet privata che consente il traffico dal blocco CIDR VCN alla porta di destinazione 8000.

Task 2: eseguire il provisioning delle istanze di OCI Compute

In questo task, eseguire il provisioning di due istanze di OCI Compute per l'impostazione del benchmark. Una è un'istanza di virtual machine (VM) solo per CPU che funge da bastion e da computer client di benchmark, l'altra è l'istanza dotata di GPU in fase di test.

  1. Esegui il provisioning di un'istanza VM.Flex.E5 con otto OCPU e Oracle Linux 8. Il client bastion/benchmark non richiede una GPU poiché invierà solo richieste al modello ospitato su un computer separato, quindi la scelta di una forma CPU. Assicurati di selezionare la subnet pubblica nei parametri di rete e non dimenticare di caricare la tua chiave pubblica SSH (o scaricare la chiave privata fornita, se preferisci).

  2. Eseguire il provisioning di un'istanza VM.GPU.A10.1. Per semplificare l'impostazione, eseguire il provisioning dell'istanza utilizzando un'immagine Oracle Linux 8 che include driver NVIDIA e il framework CUDA. Inizia selezionando la forma desiderata, quindi torna al menu di selezione delle immagini e scegli la variante Oracle Linux 8 con supporto GPU integrato. È necessario eseguire il provisioning di questa istanza nella subnet privata, il che significa che è possibile accedervi solo utilizzando l'host bastion. Assicurarsi di impostare anche la chiave SSH.

Screenshot della selezione dell'immagine Oracle Linux 8 con supporto GPU integrato

Task 3: Impostazione del client di riferimento

In questa operazione, installeremo tutti i componenti necessari per i nostri benchmark di prestazioni e configureremo l'host GPU.

  1. Eseguire il login al bastion con la chiave SSH e impostare tutti i requisiti per LLMPerf utilizzando il comando seguente.

    sudo dnf install epel-release -y
    sudo yum-config-manager --enable ol8_baseos_latest ol8_appstream ol8_addons ol8_developer_EPEL
    sudo dnf install git python3.11 python3.11-devel python3.11-pip -y
    
  2. Clonare il repository Ray LLMPerf, impostare un venv Python e installare LLMPerf utilizzando il comando seguente.

    git clone https://github.com/ray-project/llmperf.git
    cd llmperf
    mkdir venv && python3.11 -mvenv venv && source venv/bin/activate
    
  3. Prima di installare LLMPerf (e le relative dipendenze Python), modificare il file pyproject.toml e rimuovere la clausola del requisito Python. La clausola limita inutilmente la versione di Python in modo che sia minore di 3.11.

    diff --git a/pyproject.toml b/pyproject.toml
    index 7687fb2..521a2a7 100644
    --- a/pyproject.toml
    +++ b/pyproject.toml
    @@ -8,7 +8,6 @@ version = "0.1.0"
     description = "A framework for load testing LLM APIs"
     authors = [{name="Avnish Narayan", email="avnish@anyscale.com"}]
     license = {text= "Apache-2.0"}
    -requires-python = ">=3.8, <3.11"
     dependencies = ["pydantic<2.5",
                     "ray",
                     "pytest>=6.0",
    
  4. Eseguire il comando pip install -e per finalizzare la configurazione.

Task 4: Impostazione della destinazione di riferimento

In questo compito, imposteremo l'obiettivo di riferimento stesso. Poiché abbiamo già eseguito il provisioning del nodo con i driver necessari e il framework CUDA (Compute Unified Device Architecture), è sufficiente installare vLLM e le relative dipendenze python e distribuire il modello che si desidera confrontare.

Nota: l'istanza di computazione GPU di cui è stato eseguito il provisioning risiede in una subnet privata. Pertanto, per raggiungerlo, è necessario prima eseguire il login all'host bastion e impostare la chiave SSH privata scelta per la destinazione benchmark. Solo in questo modo sarà possibile eseguire il login alla destinazione benchmark utilizzando il relativo indirizzo IP privato.

  1. Installare i pacchetti necessari, ad esempio l'impostazione iniziale sull'host bastion. Aggiornare inoltre il firewall dell'host per consentire il traffico in entrata sulla porta 8000 utilizzando il comando riportato di seguito.

    sudo dnf install epel-release -y
    sudo yum-config-manager --enable ol8_baseos_latest ol8_appstream ol8_addons ol8_developer_EPEL
    sudo dnf install git python3.11 python3.11-devel python3.11-pip -y
    sudo firewall-cmd --permanent --zone=public --add-port=8000/tcp
    sudo firewall-cmd --reload
    
  2. Eseguire il comando seguente per installare vLLM e i relativi requisiti.

    mkdir venv
    python3.11 -mvenv venv && source venv/bin/activate
    pip install -U pip "bitsandbytes>=0.44.0" vllm gpustat mistral_common
    
  3. Siamo pronti ad avviare vLLM con il modello che vorremmo testare.

    export HF_TOKEN=<your huggingface token>
    export MODEL="meta-llama/Llama-3.2-3B-Instruct"
    ulimit -Sn 65536 # increase limits to avoid running out of files that can be opened
    vllm serve $MODEL --tokenizer-mode auto --config-format hf --load-format auto \
                      --enforce-eager --max-model-len 65536
    

    Nota: è necessario ridurre la lunghezza del contesto del modello per adattarla alla memoria GPU A10. La lunghezza del contesto viene ridotta dalla dimensione predefinita dei token 128k ai token 64k. Dopo il caricamento, il modello vLLM dovrebbe iniziare a produrre le sue statistiche di inferenza a intervalli regolari.

    INFO 12-09 15:46:36 metrics.py:449] Avg prompt throughput: 0.0 tokens/s, Avg generation throughput: 0.0 tokens/s, Running: 0 reqs, Swapped: 0 reqs, Pending: 0 reqs, GPU KV cache usage: 0.0%, CPU KV cache usage: 0.0%.
    
  4. Lasciamo che il server continui a funzionare in background mentre passiamo a un altro terminale dove ora testeremo e confronteremo la nostra configurazione. È possibile eseguire un test semplice con curl come indicato di seguito.

    export HOST="<the ip address of the benchmark machine"
    export MODEL="meta-llama/Llama-3.2-3B-Instruct"
    curl --location "http://${HOST}:8000/v1/chat/completions"     --header 'Content-Type: application/json'     --header 'Authorization: Bearer token'     --data '{
            "model": "'"$MODEL"'",
            "messages": [
              {
                "role": "user",
                "content": "What is the question to the answer to the ultimate question of life, the universe, and everything. You may give a humorous response."
              }
            ]
        }' -s | jq
    

    L'output sarà:

    {
      "id": "chatcmpl-f11306f943604d41bad84be1dadd9da6",
      "object": "chat.completion",
      "created": 1733997638,
      "model": "meta-llama/Llama-3.2-3B-Instruct",
      "choices": [
        {
          "index": 0,
          "message": {
            "role": "assistant",
            "content": "You want to know the ultimate question to the answer of 42?\n\nWell, after years of intense research and contemplation, I've discovered that the answer is actually a giant, cosmic joke. The question is: \"What's for lunch?\"",
            "tool_calls": []
          },
          "logprobs": null,
          "finish_reason": "stop",
          "stop_reason": null
        }
      ],
      "usage": {
        "prompt_tokens": 62,
        "total_tokens": 112,
        "completion_tokens": 50,
        "prompt_tokens_details": null
      },
      "prompt_logprobs": null
    }
    

Task 5: Esegui il benchmark

Ora siamo pronti a gestire il nostro benchmark. Dato un particolare scenario applicativo con un modello di linguaggio di grandi dimensioni scelto, ci piacerebbe capire sul sistema di destinazione quali sono le caratteristiche prestazionali dell'esecuzione di richieste di inferenza concorrenti.

Analizziamo il seguente scenario:

PARAMETER VALORE
MODELLO Strumento Meta LLaMa 3.2 3B
CASO D'USO chat
TOKEN DI INPUT N(200, 40)
TOKEN DI OUTPUT N(100, 10)
RICHIESTE CONCORRENTI 1 - 32
  1. Creare lo script seguente denominato llm_benchmark.sh sul client benchmark (host bastion) all'interno della directory home.

    #!/usr/bin/env bash
    
    set -xe
    
    # Use vLLM OpenAPI endpoint
    export OPENAI_API_BASE="http://<benchmark host>:8000/v1"
    # API key is not in use, but needs to be set for llmbench
    export OPENAI_API_KEY="none"
    
    model="meta-llama/Llama-3.2-3B-Instruct"
    
    modelname="${model##*/}"
    mkdir "$modelname"
    
    concurrent_requests=(1 2 4 8 16 32)
    
    pushd llmperf
    
    source venv/bin/activate
    
    for cr in "${concurrent_requests[@]}"
    do
        python3 token_benchmark_ray.py --model $model \
            --mean-input-tokens 200 --stddev-input-tokens 40 \
            --mean-output-tokens 100 --stddev-output-tokens 10 \
            --max-num-completed-requests $((cr * 100)) --num-concurrent-requests $cr \
            --metadata "use_case=chatbot" \
            --timeout 1800 --results-dir "../$modelname/result_outputs_chat_creq_$cr" --llm-api openai
    done
    popd
    

    Questo script consente di eseguire automaticamente una serie di benchmark con llmperf con un numero crescente di richieste concorrenti (a partire da 1 e raddoppiando successivamente fino a 32). Come si può vedere dagli argomenti passati allo script token_benchmark_ray.py stiamo impostando input e output di token come definito nella tabella precedente.

  2. Eseguire lo script di benchmark utilizzando il comando seguente.

    bash -x llm_benchmark.sh
    

Una volta fatto, troverai una nuova directory chiamata Llama-3.2-3B-Instruct nella tua home directory, dove tutti i risultati dell'esperimento verranno memorizzati in formato JSON, che puoi scaricare e post-elaborare utilizzando il tuo strumento di analisi dei dati preferito.

Nota: un modo semplice per trasformare i benchmark in un grafico consiste nell'estrarre le figure che più ti interessano utilizzando un piccolo script shell e jq in formato .csv, che possono essere facilmente copiate in Excel.

echo "concurrent_requests,token_throughput"
for i in *; do
    cat $i/*_summary.json | jq -r '[.num_concurrent_requests, .results_request_output_throughput_token_per_s_mean] | join(",")'
done;

Conferme

Altre risorse di apprendimento

Esplora altri laboratori su docs.oracle.com/learn o accedi a più contenuti gratuiti sulla formazione su Oracle Learning YouTube channel. Inoltre, visita education.oracle.com/learning-explorer per diventare un Oracle Learning Explorer.

Per la documentazione del prodotto, visita l'Oracle Help Center.