Ollama vs vLLM:并发性能深度评测

3,116 阅读14分钟

Ollama vs vLLM:并发性能深度评测

[toc]

引言

随着大语言模型(LLM)在各个领域的广泛应用,高效的推理引擎成为了部署这些模型的关键。Ollama 和 vLLM 作为当前最流行的两种推理引擎,各自有着独特的优势和适用场景。

本文将从并发性能的角度,对 Ollama 和 vLLM 进行深度对比,帮助读者更好地选择适合自己的推理框架。

一、背景介绍

1.1 Ollama

Ollama 是一个简单易用的 LLM 部署工具,以其简洁的安装和用户友好的界面而闻名。它支持多种模型架构,并提供了丰富的命令行工具和图形化界面,适合快速原型设计和小规模部署。

1.2 vLLM

vLLM(Very Large Language Model)是一个高性能的推理引擎,专注于大规模语言模型的高效推理。它通过动态批处理、显存优化和多 GPU 支持,显著提升了推理速度和资源利用率。

二、前期准备

测试环境准备

GPU型号:单块 NVIDIA GeForce RTX 4090 显卡

Ollama部署模型:同参数的 DeepSeek-R1 量化模型

vLLM部署模型: 同参数的 DeepSeek-R1-Distill-Qwen 蒸馏模型

Locust 压测工具

Locust 是一个强大的、易于使用的开源压测工具,它允许你编写 Python 脚本模拟大量并发用户进行压力测试。最棒的是,Locust 提供了一个直观的 Web UI,让你可以通过浏览器实时查看性能数据和调整测试参数。

测试报告图表说明

测试的 Charts 图表包含:

1、Total Request per Second :每秒的请求总数,横轴为时间轴,纵轴为每秒请求的数量(请求处理通过的)。

  • 绿色线:每秒钟请求成功的个数
  • 红色线:每秒钟请求失败的个数

2、Response Time :响应时间,横轴为时间轴,纵轴为以毫秒为单位的响应时间。需要注意的是,图表上面两根线并不是表示平均值,而是响应时间的“中位数”和“95%百分位数值”。

  • 绿色线:表示中位数
  • 黄色线:表示95%百分位数值

3、Number of Users :用户数,横轴为时间轴,纵轴为时间所对应的“用户数”。

———————————————— Locust性能测试之快速入门 - xyztank - 博客园

编写压测脚本

示例 python 代码:

import csv
import traceback
from locust import HttpUser, task, between
import logging


def init_loging_config():
    level = logging.INFO
    logging.basicConfig(
        level=level,
        format="%(asctime)s %(name)s %(levelname)s %(message)s ",
        datefmt='%Y-%m-%d  %H:%M:%S'
    )
    _logger = logging.getLogger(" =>")
    _logger.setLevel(level)
    return _logger


logger = init_loging_config()
req_type = "1"
api_key = "app-aRohuiTGaRFderfMS27EEfS2"
headers = {
            "Authorization": "Bearer " + api_key,
            "Content-Type": "application/json"
        }
input_file = r'api-get\data\content_1.csv'
infile = open(input_file, mode='r', encoding='utf-8')
reader = csv.reader(infile)
next(reader)  # 读取并跳过第一行(表头)


class QuickstartLLM(HttpUser):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def read_next_row(self):
        try:
            return next(reader)[1]
        except StopIteration:
            infile.close()
            return None

    def get_data(self, query: str, type: str):
        return {
            "inputs": {
                "query": query,
                "type": type
            },
            "response_mode": "blocking",
            "user": "abc-12345"
        }

    def send_post_request(self):
        return self.get_data(self.read_next_row(), req_type)

    wait_time = between(1, 3)  # 每个任务/用户之间的间隔时间(秒)
    @task
    def generate_text(self):
        data = self.send_post_request()
        try:
            response = self.client.post("/v1/workflows/run", headers=headers, json=data, timeout=120)
            logger.info(f"状态码: {response.status_code},输入文本: {data},响应结果: {response.text}")
        except Exception as e:
            logger.error(traceback.format_exc())

通过终端输入命令启动 locust:

locust -f api-get/test/locust_test.py --host http://172.10.2.122

三、性能测试

推理速度是衡量推理引擎性能的关键指标之一。以下是对 Ollama 和 vLLM 在不同模型下的推理速度测试结果:

vLLM

单线程测试

设置用户数:1,用户孵化率:1

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run20078008910.67597519013484.350.10
Aggregated20078008910.67597519013484.350.10

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

image-20250217154956617

5个线程测试

设置用户数:5,用户孵化率:1

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run20080008930.52640815561483.240.50
Aggregated20080008930.52640815561483.240.50

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

10个线程测试

设置用户数:10,用户孵化率:1

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run20094009453.53657211864489.70.90
Aggregated20094009453.53657211864489.70.90

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

20个线程测试

设置用户数:20,用户孵化率:2

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run200990010319.91726415798496.6310
Aggregated200990010319.91726415798496.6310

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

Ollama

单线程测试

设置用户数:1,用户孵化率:1

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run20063006782.01398915562489.150.10
Aggregated20063006782.01398915562489.150.10

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

5个线程测试

设置用户数:5,用户孵化率:1

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run20086008566.16426112971482.810.30
Aggregated20086008566.16426112971482.810.30

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

10个线程测试

设置用户数:10,用户孵化率:1

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run2001100010935.86577921193484.760.70
Aggregated2001100010935.86577921193484.760.70

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

20个线程测试

设置用户数:20,用户孵化率:2

Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
POST/v1/workflows/run2001600016845.03621426838487.570.60
Aggregated2001600016845.03621426838487.570.60

处理请求GPU瞬时负载:

测试结果图表输出 (图表依次为:每秒请求个数RPS,请求响应时间,用户/线程数):

汇总测试结果

推理工具请求并发数Type (请求类型)Name(请求路径)Requests(请求成功次数)Fails(请求失败次数)Median(响应时间中位数-ms)Average (平均值-ms)Min (最小响应时间-ms)Max (最大响应时间-ms)Average size (请求平均数据大小-bytes)Current RPS(每秒处理事务数)Current Failures/s(每秒请求失败数)
vLLM1POST/v1/workflows/run20078008910.67597519013484.350.10
Aggregated20078008910.67597519013484.350.10
5POST/v1/workflows/run20080008930.52640815561483.240.50
Aggregated20080008930.52640815561483.240.50
10POST/v1/workflows/run20094009453.53657211864489.70.90
Aggregated20094009453.53657211864489.70.90
20POST/v1/workflows/run200990010319.91726415798496.6310
Aggregated200990010319.91726415798496.6310
Ollama1POST/v1/workflows/run20063006782.01398915562489.150.10
Aggregated20063006782.01398915562489.150.10
5POST/v1/workflows/run20086008566.16426112971482.810.30
Aggregated20086008566.16426112971482.810.30
10POST/v1/workflows/run2001100010935.86577921193484.760.70
Aggregated2001100010935.86577921193484.760.70
20POST/v1/workflows/run2001600016845.03621426838487.570.60
Aggregated2001600016845.03621426838487.570.60

四、并发性能结论

测试环境

-- 测试工具:Locust

-- 测试路径:/v1/workflows/run

-- 请求类型:POST

-- 测试数据:20 次请求,每次请求的大小和内容保持一致。

-- 并发数:1, 5, 10, 20

测试指标

  1. Requests(请求成功次数):成功完成的请求总数。

  2. Fails(请求失败次数):失败的请求总数。

  3. Median(响应时间中位数-ms):所有响应时间的中位数。

  4. Average(平均响应时间-ms):所有响应时间的平均值。

  5. Min(最小响应时间-ms):最短响应时间。

  6. Max(最大响应时间-ms):最长响应时间。

  7. Average size(请求平均数据大小-bytes):每次请求的平均数据大小。

  8. Current RPS(每秒处理事务数):当前的吞吐量。

  9. Current Failures/s(每秒请求失败数):当前的失败率。

测试结果分析

1.请求成功率

vLLM 和 Ollama 在所有并发数下均未出现请求失败(Fails = 0),表明两者在测试环境下均具有较高的稳定性。

2.平均响应时间

vLLM的平均响应时间随着并发数的增加而逐渐上升:

  • 并发数 1:8910.67 ms

  • 并发数 5:8930.52 ms

  • 并发数 10:9453.53 ms

  • 并发数 20:10319.91 ms

Ollama的平均响应时间同样随着并发数的增加而上升,但整体响应时间较 vLLM 更低:

  • 并发数 1:6782.01 ms

  • 并发数 5:8566.16 ms

  • 并发数 10:10935.86 ms

  • 并发数 20:16845.03 ms

结论:在低并发场景下(并发数 1 和 5),Ollama 的平均响应时间显著优于 vLLM。然而,随着并发数的增加,Ollama 的响应时间增长速度更快,尤其是在并发数 20 时,其平均响应时间几乎是 vLLM 的两倍。

3.响应时间中位数

vLLM的响应时间中位数:

  • 并发数 1:7800 ms

  • 并发数 5:8000 ms

  • 并发数 10:9400 ms

  • 并发数 20:9900 ms

Ollama的响应时间中位数:

  • 并发数 1:6300 ms

  • 并发数 5:8600 ms

  • 并发数 10:11000 ms

  • 并发数 20:16000 ms

结论:在低并发场景下,Ollama 的响应时间中位数优于 vLLM。然而,随着并发数的增加,Ollama 的响应时间中位数增长速度更快,尤其是在并发数 20 时,其响应时间中位数显著高于 vLLM。

4.吞吐量(RPS)

vLLM的吞吐量随着并发数的增加而逐渐上升:

  • 并发数 1:0.1 RPS

  • 并发数 5:0.5 RPS

  • 并发数 10:0.9 RPS

  • 并发数 20:1.0 RPS

Ollama的吞吐量同样随着并发数的增加而上升,但整体吞吐量较 vLLM 更低:

  • 并发数 1:0.1 RPS

  • 并发数 5:0.3 RPS

  • 并发数 10:0.7 RPS

  • 并发数 20:0.6 RPS

结论:vLLM 在所有并发数下的吞吐量均优于 Ollama,尤其是在高并发场景下(并发数 10 和 20),vLLM 的吞吐量显著高于 Ollama。

5.最大响应时间

vLLM的最大响应时间:

  • 并发数 1:19013 ms

  • 并发数 5:15561 ms

  • 并发数 10:11864 ms

  • 并发数 20:15798 ms

Ollama的最大响应时间:

  • 并发数 1:15562 ms

  • 并发数 5:12971 ms

  • 并发数 10:21193 ms

  • 并发数 20:26838 ms

结论:vLLM 的最大响应时间在低并发场景下略高于 Ollama,但在高并发场景下,vLLM 的最大响应时间显著低于 Ollama。这表明 vLLM 在高并发场景下具有更好的性能稳定性。

结论总结

在并发性能测试中,vLLM 和 Ollama 均表现出较高的稳定性(无失败请求),但在性能表现上存在显著差异:

1、低并发场景(并发数 1 和 5):

  • Ollama 的平均响应时间和响应时间中位数显著优于 vLLM。

  • vLLM 的吞吐量略高于 Ollama。

2、高并发场景(并发数 10 和 20):

  • vLLM 的平均响应时间和响应时间中位数显著优于 Ollama。
  • vLLM 的吞吐量显著高于 Ollama。
  • vLLM 的最大响应时间在高并发场景下更稳定,表明其在高负载下的性能表现更优。

结论:vLLM 在高并发场景下表现更优,适合需要处理大规模并发请求的应用场景。而 Ollama 在低并发场景下具有较低的响应时间,适合对响应速度要求较高的低负载场景。

重要说明

  1. 本次测试的逻辑较为简化,部分操作未考虑线程安全性,可能导致结果存在一定的偏差。
  2. 由于 Ollama 的 DeepSeek-R1 量化模型与 DeepSeek-R1-Distill-Qwen 蒸馏模型在架构上存在差异,其响应时间的对比并不完全具备参考性。
  3. 此外,测试中使用的输入文本不同也会对响应时间产生显著影响。

因此,相关测试结果仅用于大致对比,不能作为性能评估的唯一依据,实际部署和使用时应以真实环境中的表现为准。