部署使用vllm和sglang部署Qwen3-30B-A3B

Qwen3-30B-A3B部署(使用vllm和sglang)

  • 模型:Qwen3-30B-A3B

  • 显卡:4090 4 张

  • python版本

    python 3.12

  • 重要包的版本

    vllm==0.8.6
    sglang== 0.4.6.post1
    
    

前排提示,文末有大模型AGI-CSDN独家资料包哦!


安装

模型下载
  • 使用modelscope下载,需要安装modelscope库

    pip install modelscope
    
    

    已经有modelscope库的需要升级下面的几个包

    pip install --upgrade modelscope -i https://pypi.tuna.tsinghua.edu.cn/simple
    
    pip install --upgrade transformers -i https://pypi.tuna.tsinghua.edu.cn/simple
    pip install --upgrade peft -i https://pypi.tuna.tsinghua.edu.cn/simple
    pip install --upgrade diffusers -i https://pypi.tuna.tsinghua.edu.cn/simple
    
    
  • 下载

    默认下载在当前用户的.cache文件夹下,比如现在是root用户,则默认在

    /root/.cache/modelscope/hub/models/Qwen/Qwen3-30B-A3B

    我们希望将其下载在

    /root/Qwen/Qwen3-30B-A3B

    from modelscope.hub.snapshot_download import snapshot_download
    
    model_name = "Qwen/Qwen3-30B-A3B"
    
    cache_dir = "/root"  # 替换为你希望的路径
    
    snapshot_download(model_name, cache_dir=cache_dir)
    
    
    

    image-20250430150442295

  • 下载完成后查看下

    ls -lha /root/Qwen/Qwen3-30B-A3B 
    
    

    image-20250430150545614

  • 按照官方提供的测试代码就是如下的,不过我们后续使用vllm和sglan启动

    from modelscope import AutoModelForCausalLM, AutoTokenizer
    import time
    
    model_name = "/root/Qwen/Qwen3-30B-A3B" 
    cache_dir = '/root'
    
    tokenizer = AutoTokenizer.from_pretrained(model_name,local_files_only=True)
    
    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        torch_dtype="auto",
        device_map="auto",
        low_cpu_mem_usage=True,
        local_files_only=True
    )
    
    prompt = "介绍下你自己."
    messages = [
        {"role": "user", "content": prompt}
    ]
    
    text = tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True,
        enable_thinking=True # Switch between thinking an non-thinking modes, Default is True
    )
    
    model_inputs = tokenizer([text], return_tensors="pt").to(model.device)
    # conduct text completion
    start_time = time.time()
    generated_ids = model.generate(
        **model_inputs,
        max_new_tokens=32768
    )
    end_time = time.time() - start_time()
    output_ids = generated_ids[0][len(model_inputs.input_ids[0]):].tolist()
    # parsing thinking content
    try:
        # rindex finding 151668</think>
        index = len(output_ids) -output_ids[::-1].index(151668)
    except ValueError:
        index=0
    
    thinking_content = tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("\n")
    content = tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("\n")
    print("thinking content:", thinking_content)
    print("content:", content)
    
    

环境安装
  • 使用conda创建虚拟环境

    conda create -n sglang python=3.12
    
    conda activate sglang
    
    
  • 下载vllm(指定清华源,否则极慢)

    pip install vllm -i https://pypi.tuna.tsinghua.edu.cn/simple
    
    
  • 下载sglang

    pip install sglang -i https://pypi.tuna.tsinghua.edu.cn/simple
    pip install sgl_kernel -i https://pypi.tuna.tsinghua.edu.cn/simple
    
    
  • 后续使用中一些其他报错需要装的包

    pip install orjson
    pip install torchao
    
    

vllm启动

  • vllm启动命令

    CUDA_VISIBLE_DEVICES=0,1,2,3 \
    vllm serve /root/Qwen/Qwen3-30B-A3B \
      --tensor-parallel-size 4 \
      --max-model-len 32768 \
      --gpu-memory-utilization 0.8 \
      --host 0.0.0.0 \
      --port 8081 \
      //--enable-reasoning --reasoning-parser deepseek_r1 \
      --served-model-name Qwen3-30B-A3B-vllm
    
    

    以下是对VLLM启动命令参数的简要说明

    参数简要说明
    CUDA_VISIBLE_DEVICES=0,1,2,3指定要使用的GPU设备编号,这里选择使用0、1、2、3四个GPU设备
    vllm serve /root/Qwen/Qwen3-30B-A3B启动VLLM服务、指定模型路径
    --tensor-parallel-size张量并行大小
    --max-model-le模型处理的最大序列长度
    --gpu-memory-utilization预分配的GPU内存比例 (vllm默认为0.9)
    --host设置服务监听的主机地址,0.0.0.0表示监听所有网络接口
    --port设置服务监听的端口号
    --enable-reasoning启用推理功能(think)
    --reasoning-parser指定推理解析器
    --served-model-nam设置模型名
  • 以8081端口启动成功

    image-20250430160957293

  • 显存占用情况

    image-20250430161014493

  • 测试

    测试代码

    from openai import OpenAI
    import openai
    
    openai.api_key = '1111111' # 这里随便填一个
    openai.base_url = 'http://127.0.0.1:8081/v1'
    
    def get_completion(prompt, model="QwQ-32B"):
        client = OpenAI(api_key=openai.api_key,
                        base_url=openai.base_url
                        )
        messages = [{"role": "user", "content": prompt}]
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            stream=False
        )
        return response.choices[0].message.content
    	
    prompt = '请计算straberry这个单词中字母r的出现次数'
    
    response = get_completion(prompt, 
                              model="Qwen3-30B-A3B-vllm"
    			              )
    print(response)
    
    
    

    image-20250430161750885

    能到 100tokens/s

    image-20250430162439992


sglang启动

  • sglang启动命令

    CUDA_VISIBLE_DEVICES=0,1,2,3 python -m sglang.launch_server \
      --model-path /root/Qwen/Qwen3-30B-A3B \
      --tp 4 \
      --max-prefill-tokens 32768 \
      --mem-fraction-static 0.8 \
      --host 0.0.0.0 \
      --port 8081 \
      --served-model-name Qwen3-30B-A3B
    
    
    

    参数说明:

    参数简要说明
    CUDA_VISIBLE_DEVICES=0,1,2,3指定要使用的GPU设备编号,这里选择使用0、1、2、3四个GPU设备
    python -m sglang.launch_server启动SGLang服务的主命令
    --model-path指定模型权重的路径
    可以是本地文件夹或Hugging Face仓库ID
    --tp张量并行大小
    --max-prefill-tokens最大token长度
    --mem-fraction-staticGPU内存使用比例
    --hos设置服务监听的主机地址,0.0.0.0表示监听所有网络接口
    --port 8081设置服务监听的端口号
    --served-model-name模型名称(在API响应中使用)
  • 以8081端口启动成功

    image-20250430154300449

  • 测试代码

    from openai import OpenAI
    import openai
    
    openai.api_key = '1111111' # 这里随便填一个
    openai.base_url = 'http://127.0.0.1:8081/v1'
    
    def get_completion(prompt, model="QwQ-32B"):
        client = OpenAI(api_key=openai.api_key,
                        base_url=openai.base_url
                        )
        messages = [{"role": "user", "content": prompt}]
        response = client.chat.completions.create(
            model=model,
            messages=messages,
            stream=False
        )
        return response.choices[0].message.content
    	
    prompt = '请计算straberry这个单词中字母r的出现次数'
    
    response = get_completion(prompt, 
                              model="Qwen3-30B-A3B"
    			              )
    print(response)
    
    
    

    image-20250430154345491

  • 显存占用情况

    image-20250430223528716

  • 平均可以达到160 tokens /s

    image-20250430154411495


modelscope压测

压测结果
  • modelscope压测的方法可以看之前的博客

    https://blog.csdn.net/hbkybkzw/article/details/147641988

  • GPU机器4卡4090,在自己电脑使用modelscope分别对vllm和sglang进行以下并发数的压测,都统一用的开放数据集

    并发数列表

    parallel_list = [1,5,10,20,30,40,50]
    
    
  • vllm压测代码(使用---.--.--.---隐藏了GPU机器的ip )

    from evalscope.perf.main import run_perf_benchmark
    
    def run_perf(parallel,model_name):
        task_cfg = {
            'url': 'http://---.--.--.---:8081/v1/chat/completions',
            'parallel': parallel,
            'model': model_name,
            'number': 100,
            'api': 'openai',
            'dataset': 'openqa',
            'stream': True,
            'debug': False,
        }
        run_perf_benchmark(task_cfg)
    
    model_name = 'Qwen3-30B-A3B-vllm' # vllm启动时设置的model_name
    parallel_list = [1,5,10,20,30,40,50]
    
    if __name__ == '__main__':
        for parallel in parallel_list:
            run_perf(parallel,model_name)
    
    
    
    
  • sglang压测代码(使用---.--.--.---隐藏了GPU机器的ip )

    from evalscope.perf.main import run_perf_benchmark
    
    def run_perf(parallel,model_name):
        task_cfg = {
            'url': 'http://---.--.--.---:8081/v1/chat/completions',
            'parallel': parallel,
            'model': model_name,
            'number': 100,
            'api': 'openai',
            'dataset': 'openqa',
    	'stream': True,
            'debug': False,
        }
        run_perf_benchmark(task_cfg)
    
    model_name = 'Qwen3-30B-A3B' # sglang启动时设置的model_name
    parallel_list = [1,5,10,20,30,40,50]
    
    if __name__ == '__main__':
        for parallel in parallel_list:
            run_perf(parallel,model_name)
    
    
    
  • 整理了下压测结果如下

    推理引擎并发数总请求数成功数失败数总时长每秒输出tokens每秒总tokensqps平均用时平均首帧用时生成每个token的平均时间平均输入token长度平均输出token长度
    vllm110010001120.52110.20112.780.0911.200.150.0128.891234.81
    sglang11001000753.52162.47166.300.137.530.190.0128.891224.24
    vllm51001000375.81323.67331.350.2718.570.170.0228.891216.38
    sglang51001000306.32402.64412.070.3315.150.230.0128.891233.38
    vllm101001000282.89428.24438.460.3527.690.180.0228.891211.45
    sglang101001000234.75509.23521.540.4323.030.220.0228.891195.40
    vllm201001000189.23654.34669.610.5335.920.230.0328.891238.22
    sglang201001000159.30755.89774.020.6330.050.260.0228.891204.13
    vllm301001000145.56843.25863.100.6938.700.250.0328.891227.45
    sglang301001000133.24926.01947.690.7535.780.260.0328.891233.78
    vllm401001000132.34924.52946.350.7645.430.290.0428.891223.48
    sglang401001000122.121002.871026.520.8241.820.410.0328.891224.73
    vllm501001000133.63917.30938.920.7555.640.330.0528.891225.82
    sglang501001000119.471014.741038.920.8450.430.290.0428.891212.29

压测结果可视化
  • 每秒输出tokens对比

    image-20250502000343020

  • QPS对比

    image-20250502000408051

  • 平均用时对比

    image-20250502000428280

  • 首帧用时对比

    image-20250502000627462


压测结果分析
  • 针对vllm和sglang两种推理引擎在不同并发数下的性能进行了分析。

    1. 吞吐量分析(每秒输出tokens)

      vllm:随着并发数从1增加到30,每秒输出tokens从110.20提升到843.25,提升了约7.7倍。当并发数达到40时,性能达到峰值924.52。当并发数增加到50时,性能略有下降至917.30。

      sglang:随着并发数从1增加到50,每秒输出tokens持续增长,从162.47提升到1014.74,提升了约6.2倍。在所有并发数下,sglang的吞吐量均高于vllm。

    2. 响应时间分析

      平均用时:两种引擎的平均用时都随着并发数增加而增加,但sglang在各个并发数下的平均用时均低于vllm。

      平均首帧用时:两种引擎的首帧用时差异不大,但随着并发数增加,首帧用时也有所增加。

    3. 性能提升百分比

      sglang相对于vllm的性能提升:

      并发数每秒输出tokens提升平均用时改善
      147.4%32.8%
      524.4%18.4%
      1018.9%16.8%
      2015.5%16.3%
      309.8%7.5%
      408.5%7.9%
      5010.6%9.4%
    4. 稳定性分析

      从数据来看,两种引擎在所有测试的并发数下(1-50)都保持了100%的成功率,没有失败请求。但从性能曲线来看:

      vllm:在并发数为40时达到性能峰值(每秒输出tokens为924.52)当并发数增加到50时,性能略有下降(每秒输出tokens为917.30,下降约0.8%)平均用时从40并发的45.43秒增加到50并发的55.64秒,增加了22.5%

      sglang:在测试范围内(1-50并发),性能一直呈上升趋势,50并发时每秒输出tokens达到1014.74,比40并发时的1002.87略有提升(约1.2%),平均用时从40并发的41.82秒增加到50并发的50.43秒,增加了20.6%

结论
  • 在4卡3090下,vllm和sglang
  1. 性能对比:在所有测试的并发数下,sglang的性能均优于vllm,无论是吞吐量还是响应时间。

  2. 稳定支持的并发数

    vllm:可以稳定支持40并发,此时性能达到峰值,超过此并发数性能开始下降。

    sglang:在测试范围内(1-50并发)都表现稳定,且性能仍有上升趋势,可以稳定支持50并发。若需确定其极限,可以进行更高并发数的测试。

  • 选择建议

    如果追求更高的吞吐量和更低的响应时间,sglang是更好的选择。

    如果系统需要支持40以上的并发,sglang的优势更为明显。

    两种引擎在低并发数(如1-10)下的性能差异更大,如果主要在低并发场景使用,sglang的优势更为突出。

  • 总体而言,sglang在各项指标上均优于vllm,特别是在高并发场景下表现更为稳定,是部署Qwen3-30B-A3B模型的更佳选择。


CSDN独家福利

最后,感谢每一个认真阅读我文章的人,礼尚往来总是要有的,下面资料虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

### 部署 DeepSeek-R1-Distill-Qwen-14B 模型使用 vLLM 框架 为了成功部署 `DeepSeek-R1-Distill-Qwen-14B` 模型,需先完成环境配置并下载所需模型文件。具体操作如下: #### 下载模型 通过 Python 脚本可以方便地获取指定版本的预训练模型。创建名为 `model_download.py` 的脚本,并加入以下代码片段来实现自动化下载过程[^2]。 ```python from modelscope import snapshot_download model_dir = snapshot_download( &#39;deepseek-ai/DeepSeek-R1-Distill-Qwen-14B&#39;, local_dir=&#39;deepseek-ai/DeepSeek-R1-Distill-Qwen-14B&#39;, revision=&#39;master&#39; ) print(f"Model downloaded successfully to {model_dir}") ``` 执行上述脚本后,将在本地目录下获得完整的模型结构及相关资源文件。 #### 安装依赖项 确保安装了最新版的 `transformers`, `torch`, 及其他必要的库。对于特定于 vLLM 的需求,则需要额外引入该框架及其配套工具包。 ```bash pip install transformers torch vllm ``` #### 加载与启动服务 利用 vLLM 提供的功能接口加载已下载好的大语言模型实例,并将其设置为在线推理服务器模式运行。下面是一个简单的例子展示如何初始化暴露 REST API 接口用于外部访问。 ```python import uvicorn from fastapi import FastAPI, Request from transformers import AutoTokenizer, pipeline from vllm import LLMEngine app = FastAPI() tokenizer = AutoTokenizer.from_pretrained(model_dir) @app.post("/predict") async def predict(request: Request): data = await request.json() input_text = data.get("text", "") engine = LLMEngine.from_pretrained(model_dir) prompt_tokens = tokenizer(input_text, return_tensors="pt").input_ids outputs = engine.generate(prompt_tokens)[0] generated_text = tokenizer.decode(outputs, skip_special_tokens=True) return {"generated_text": generated_text} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) ``` 这段代码实现了基本的服务端逻辑,允许客户端发送 POST 请求携带待处理文本至 `/predict` 终结点处得到即时响应结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值