LLMPerf

repository·main·Indexed 22 days ago

https://github.com/ray-project/llmperf

A framework for load testing and evaluating the performance and correctness of LLM APIs. LLMPerf measures latency and throughput via tools like token_benchmark_ray.py and verifies model accuracy with llm_correctness.py. It supports multiple providers including OpenAI, Vertex AI, Anthropic, and SageMaker, and allows for custom client implementation by subclassing LLMClient using Ray actors.

Tokens
7.1K
Snippets
19
Records
22
Agent score
77%

What's inside LLMPerf

  1. Run a Load Test with token_benchmark_ray.py

    main

    The load test evaluates LLM performance by spawning concurrent requests and measuring inter-token latency and generation throughput. It uses a prompt based on randomly sampled lines from Shakespeare sonnets and counts tokens using the LlamaTokenizer for consistency across APIs.

    To run a load test, use the token_benchmark_ray.py script. You must specify the --llm-api and provide necessary credentials via environment variables.

    Example: OpenAI Compatible API

    export OPENAI_API_KEY=secret_abcdefg
    export OPENAI_API_BASE="https://api.endpoints.anyscale.com/v1"
    
    python token_benchmark_ray.py \
    --model "meta-llama/Llama-2-7b-chat-hf" \
    --mean-input-tokens 550 \
    --stddev-input-tokens 150 \
    --mean-output-tokens 150 \
    --stddev-output-tokens 10 \
    --max-num-completed-requests 2 \
    --timeout 600 \
    --num-concurrent-requests 1 \
    --results-dir "result_outputs" \
    --llm-api openai \
    --additional-sampling-params '{}'
  2. Configure LLMPerf for Vertex AI

    main

    When using Vertex AI, the --model flag is used for logging purposes only; the actual model is selected via the VERTEXAI_ENDPOINT_ID environment variable. Because Vertex AI does not return total generated token counts, LLMPerf uses the LlamaTokenizer to count them.

    Note: The GCLOUD_ACCESS_TOKEN expires frequently (approx. 15 minutes) and must be refreshed.

    gcloud auth application-default login
    gcloud config set project YOUR_PROJECT_ID
    
    export GCLOUD_ACCESS_TOKEN=$(gcloud auth print-access-token)
    export GCLOUD_PROJECT_ID=YOUR_PROJECT_ID
    export GCLOUD_REGION=YOUR_REGION
    export VERTEXAI_ENDPOINT_ID=YOUR_ENDPOINT_ID
    
    python token_benchmark_ray.py \
    --model "meta-llama/Llama-2-7b-chat-hf" \
    --mean-input-tokens 550 \
    --stddev-input-tokens 150 \
    --mean-output-tokens 150 \
    --stddev-output-tokens 10 \
    --max-num-completed-requests 2 \
    --timeout 600 \
    --num-concurrent-requests 1 \
    --results-dir "result_outputs" \
    --llm-api "vertexai" \
    --additional-sampling-params '{}'
  3. Configure environment variables for VertexAIClient

    main

    To use the VertexAIClient, you must set the following environment variables. These variables are used to construct the API request URL and provide authentication for the Google Cloud Vertex AI endpoint.

    Required environment variables:

    • GCLOUD_PROJECT_ID: Your Google Cloud Project ID.
    • GCLOUD_REGION: The region where your endpoint is located (e.g., us-central1).
    • VERTEXAI_ENDPOINT_ID: The ID of the Vertex AI endpoint you are testing.
    • GCLOUD_ACCESS_TOKEN: A valid Google Cloud access token (typically obtained via gcloud auth print-access-token).
    export GCLOUD_PROJECT_ID=YOUR_PROJECT_ID
    export GCLOUD_REGION=YOUR_REGION
    export VERTEXAI_ENDPOINT_ID=YOUR_ENDPOINT_ID
    export GCLOUD_ACCESS_TOKEN=$(gcloud auth print-access-token)
  4. Install LLMPerf

    main

    To install LLMPerf, clone the repository and install it in editable mode using pip.

    git clone https://github.com/ray-project/llmperf.git
    cd llmperf
    pip install -e .
    ```bash
    git clone https://github.com/ray-project/llmperf.git
    cd llmperf
    pip install -e .
    ```埋
  5. Run a Correctness Test with llm_correctness.py

    main

    The correctness test evaluates how accurately an LLM converts word-formatted numbers into digits (e.g., "one hundred" to 100). It reports the number of mismatches across multiple requests.

    To run a correctness test, use the llm_correctness.py script.

    Example: Anthropic

    export ANTHROPIC_API_KEY=secret_abcdefg
    
    python llm_correctness.py \
    --model "claude-2" \
    --llm-api "anthropic"  \
    --max-num-completed-requests 5 \
    --timeout 600 \
    --num-concurrent-requests 1 \
    --results-dir "result_outputs"
  6. How SageMakerClient approximates token counts

    main

    Because Amazon SageMaker does not always return the exact number of generated tokens in its response, SageMakerClient approximates the count using a LlamaTokenizerFast (specifically the hf-internal-testing/llama-tokenizer).

    This approximation is used to calculate:

    • NUM_OUTPUT_TOKENS
    • NUM_INPUT_TOKENS
    • NUM_TOTAL_TOKENS
    • REQ_OUTPUT_THROUGHPUT
  7. Analyze individual token benchmark responses

    main

    When running token_benchmark_ray.py with the --results-dir flag, LLMPerf saves individual response data to JSON files. You can use pandas to load these files and analyze performance metrics such as input/output token counts, Time To First Token (TTFT), end-to-end latency, and generation throughput.

    To perform an analysis:

    1. Load the JSON file using pd.read_json().
    2. Filter for valid requests where error_code is not empty.
    3. Map the raw fields to a cleaner DataFrame for plotting and statistical analysis.

    Key fields in the response JSON include:

    • number_input_tokens
    • number_output_tokens
    • ttft_s (Time To First Token in seconds)
    • end_to_end_latency_s
    • request_output_throughput_token_per_s (used for generation_throughput)
    import pandas as pd
    
    # Load the individual responses JSON file
    df = pd.read_json('path/to/your_individual_responses.json')
    
    # Filter for valid responses (where error_code is not empty)
    valid_df = df[(df["error_code"] != "")]
    
    # Create a summary DataFrame for analysis
    final_df = pd.DataFrame()
    final_df["number_input_tokens"] = valid_df["number_input_tokens"]
    final_df["number_output_tokens"] = valid_df["number_output_tokens"]
    final_df["ttft_s"] = valid_df["ttft_s"]
    final_df["end_to_end_latency_s"] = valid_df["end_to_end_latency_s"]
    final_df["generation_throughput"] = valid_df["request_output_throughput_token_per_s"]
    
    # Example: Calculate means and plot TTFT vs Input Tokens
    mean_tokens_in = final_df["number_input_tokens"].mean()
    mean_tokens_out = final_df["number_output_tokens"].mean()
    print(f"Mean number of input tokens: {mean_tokens_in}. Mean number of output tokens: {mean_tokens_out}")
    final_df.plot.scatter(x="number_input_tokens", y="ttft_s", title="Number of Input Tokens vs. TTFT")
  8. Configure environment variables for SageMakerClient

    main

    To use the SageMakerClient, you must provide AWS credentials and region information via environment variables. The client will raise a ValueError if these are not set.

    Required environment variables:

    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
    • AWS_REGION_NAME
    export AWS_ACCESS_KEY_ID='your_access_key'
    export AWS_SECRET_ACCESS_KEY='your_secret_key'
    export AWS_REGION_NAME='your_region'
  9. Configure OpenAI Chat Completions client via environment variables

    main

    The OpenAIChatCompletionsClient requires two specific environment variables to be set for authentication and routing. If these are missing, the client will raise a ValueError.

    • OPENAI_API_BASE: The base URL for the OpenAI-compatible API endpoint. The client automatically appends chat/completions to this address.
    • OPENAI_API_KEY: The API key used for Bearer token authentication.
    export OPENAI_API_BASE="https://api.openai.com/v1"
    export OPENAI_API_KEY="your-api-key-here"
  10. Use the RequestsLauncher API for advanced usage

    main

    For programmatic control over LLM performance testing, use the RequestsLauncher and OpenAIChatCompletionsClient (or other client implementations) via Ray.

    When using Ray clients, you must pass environment variables (like OPENAI_API_KEY) into ray.init(runtime_env=...) to ensure the remote actors have access to them.

    import ray
    from transformers import LlamaTokenizerFast
    from llmperf.ray_clients.openai_chat_completions_client import OpenAIChatCompletionsClient
    from llmperf.models import RequestConfig
    from llmperf.requests_launcher import RequestsLauncher
    
    # Pass environment variables to ray.init for remote client access
    ray.init(runtime_env={"env_vars": {"OPENAI_API_BASE" : "https://api.endpoints.anyscale.com/v1",
                                       "OPENAI_API_KEY" : "YOUR_API_KEY"}})
    
    base_prompt = "hello_world"
    tokenizer = LlamaTokenizerFast.from_pretrained(
        "hf-internal-testing/llama-tokenizer"
    )
    base_prompt_len = len(tokenizer.encode(base_prompt))
    prompt = (base_prompt, base_prompt_len)
    
    # Create a client for spawning requests
    clients = [OpenAIChatCompletionsClient.remote()]
    
    req_launcher = RequestsLauncher(clients)
    
    req_config = RequestConfig(
        model="meta-llama/Llama-2-7b-chat-hf",
        prompt=prompt
        )
    
    req_launcher.launch_requests(req_config)
    result = req_launcher.get_next_ready(block=True)
    print(result)
    import ray
    from transformers import LlamaTokenizerFast
    
    from llmperf.ray_clients.openai_chat_completions_client import (
        OpenAIChatCompletionsClient,
    )
    from llmperf.models import RequestConfig
    from llmperf.requests_launcher import RequestsLauncher
    
    
    # Copying the environment variables and passing them to ray.init() is necessary
    # For making any clients work.
    ray.init(runtime_env={"env_vars": {"OPENAI_API_BASE" : "https://api.endpoints.anyscale.com/v1",
                                       "OPENAI_API_KEY" : "YOUR_API_KEY"}})
    
    base_prompt = "hello_world"
    tokenizer = LlamaTokenizerFast.from_pretrained(
        "hf-internal-testing/llama-tokenizer"
    )
    base_prompt_len = len(tokenizer.encode(base_prompt))
    prompt = (base_prompt, base_prompt_len)
    
    # Create a client for spawning requests
    clients = [OpenAIChatCompletionsClient.remote()]
    
    req_launcher = RequestsLauncher(clients)
    
    req_config = RequestConfig(
        model="meta-llama/Llama-2-7b-chat-hf",
        prompt=prompt
        )
    
    req_launcher.launch_requests(req_config)
    result = req_launcher.get_next_ready(block=True)
    print(result)
  11. Implement a custom LLM client by subclassing LLMClient

    main

    To add support for a new LLM provider, implement the LLMClient base class from llmperf.ray_llm_client and decorate the class with @ray.remote to turn it into a Ray actor.

    Your implementation must include the llm_request method, which handles a single completion request.

    Method Signature: llm_request(self, request_config: RequestConfig) -> Tuple[Metrics, str, RequestConfig]

    Returns:

    • Metrics: Performance characteristics of the request.
    • str: The text generated by the LLM API.
    • RequestConfig: The configuration used for the request (primarily for logging).
    from llmperf.ray_llm_client import LLMClient
    import ray
    from typing import Tuple
    # Note: You will need to import RequestConfig and Metrics from the appropriate modules
    
    @ray.remote
    class CustomLLMClient(LLMClient):
    
        def llm_request(self, request_config: RequestConfig) -> Tuple[Metrics, str, RequestConfig]:
            """Make a single completion request to a LLM API
    
            Returns: Metrics, generated text, and the request_config.
            """
            # Implementation goes here
            ...
    from llmperf.ray_llm_client import LLMClient
    import ray
    
    
    @ray.remote
    class CustomLLMClient(LLMClient):
    
        def llm_request(self, request_config: RequestConfig) -> Tuple[Metrics, str, RequestConfig]:
            """Make a single completion request to a LLM API
    
            Returns: Metrics about the performance charateristics of the request.
            The text generated by the request to the LLM API.
            The request_config used to make the request. This is mainly for logging purposes.
    
            """
            ...
  12. Configure LLM API requests with RequestConfig

    main

    The RequestConfig class defines the schema for an individual LLM API request. It is used to specify the model, the prompt (including its length), and any additional parameters required by the target API.

    Key fields:

    • model: The identifier of the model to use.
    • prompt: A tuple containing the prompt string and its integer length (prompt_text, prompt_length).
    • sampling_params: An optional dictionary of additional sampling parameters (e.g., temperature, top_p) to be sent with the request. Refer to the specific LLM API's documentation for supported keys.
    • llm_api: An optional string specifying the name of the LLM API provider.
    • metadata: An optional dictionary for attaching arbitrary metadata for logging or validation.
    from llmperf.models import RequestConfig
    
    config = RequestConfig(
        model="gpt-4",
        prompt=("What is Ray?", 12),
        sampling_params={"temperature": 0.7},
        llm_api="openai",
        metadata={"test_run": "batch_01"}
    )