veFuser 是字节跳动团队推出的生图生视频扩散模型(Diffusion Model)服务框架,实现低延迟、低成本图片和视频生成。比如,针对 Wan2.1 在 32xA100-40GB 上可以实现 20 sec 生成 5 sec 480P 的视频的极致用户体验。
框架核心能力:
算子优化:针对 D、A、H20 等卡对扩散模型常用的算子进行了深度算子调优
并行优化:利用混合序列并行(USP)、张量并行、流水并行将推理部署扩展到非常大规模。
微服务化:扩散模型工作流往往包含多个独立的模型,(VAE、Text Encoder、VAE 、LLM等)。各个阶段对显存、计算、带宽等不同资源瓶颈,在成本最优的异构资源上的实现分布式部署,并且通过 veTurbo rpc 实现多角色的通信。
内存优化:深度内存优化,比如可以将 14B Wan2.1 部署在 24GB D 卡上。
DiT(Diffusion Transformer)模型在推理过程中面临多个关键痛点,主要体现在计算效率、显存占用、模型架构复杂性及多模态融合等方面。具体如下:
序列长度与Attention计算量:DiT模型在处理高分辨率图像或长视频时,输入序列长度显著增长,导致自注意力(Self-Attention)机制的计算量呈平方级膨胀。
扩散步骤的冗余性:扩散模型需要多步迭代去噪(通常50-1000步),每一步均需完整的前向计算,累积计算开销巨大。
架构多样性:不同 DiT 模型的算子设计和连接方式差异显著。增加了并行策略适配的复杂性。同时不同阶段算子对硬件设备的计算和显存特性要求不同,存在极大差异,导致同构推理性价比低下。例如,DiT是计算密集型算子,而 VAE 则需要极高的显存与访存带宽。
并行策略不足:传统LLM推理框架(如vLLM)的并行方法(如张量并行)在DiT中效率低,需混合流水线并行(PipeFusion)、序列并行(USP)等创新方案,但实现难度高且缺乏统一框架。
视频生成的实时性瓶颈:基于DiT的视频生成模型(如Sora)因多帧连贯性要求,需处理时空一致性,导致单卡推理难以满足实时需求。
长序列处理的扩展性:DiT的长序列(如百万级token)在分布式系统中面临负载均衡和通信效率问题,需混合并行策略(如PipeFusion + Sequence Parallel)优化,但对网络带宽要求苛刻。
为了满足解决 DiT 推理场景的常见痛点,字节跳动团队推出了 veFuser。其主要解决了分离架构调度、显存优化、并行计算和通信传输效率等方面的关键挑战。
分布式分离架构: 将耦合在同一个 pipeline 中的流程(如Encoder、VAE、DiT等),从模型部署的粒度,拆分为独立的微服务,降低生成延时。同时结合各个阶段对显存、计算、带宽等不同资源瓶颈,利用异构硬件的不同特性,优化部署成本。
高效并行框架:支持混合流水线并行(PipeFusion)、ulysses并行(USP 并行)和 CFG 并行,显著提升多卡扩展性。
通信效率提升: 通过 veTurbo rpc (支持在 vpc 上实现虚拟 RDMA 传输通信协议)实现多角色的通信,同时针对 tensor 结构做了传输性能优化。
灵活可扩展: 支持自定义 pipeline 和服务组件,支持不同类型的模型推理的低成本接入。
无损量化: 优化 attention 算子实现无损 FP8 计算,降低计算开销。量化权重,降低显存开销。
I2V 性能相较于业内 SOTA 水平延时性能提升 110%~120%,480P 每 infer-steps 平均 1.8 秒;720P 每infer-steps 平均 5 秒。 T2V 性能相较于业内 SOTA 水平延时性能提升 150%~153%,480P 每 infer-steps 平均 1.5 秒;720P 每 infer-steps 平均 4 秒。
测试模型:Wan-14B
机器分布:
I2V 延时分布:
画面比例 | 宽 | 高 | 帧率 | step | 并行 | 视频时长 | 优化 | I2V视频生成耗时(单位:s) | 公式计算的tokens |
---|---|---|---|---|---|---|---|---|---|
16:9 | 1280 | 720 | 16 | 40 | SP8 | 5秒 | 业内 SOTA | 430 | 74256 |
16:9 | 1280 | 720 | 16 | 40 | SP8 | 5秒 | veFuser | 203 | 74256 |
16:9 | 832 | 480 | 16 | 40 | SP8 | 5秒 | 业内 SOTA | 160 | 32136 |
16:9 | 832 | 480 | 16 | 40 | SP8 | 5秒 | veFuser | 73 | 32136 |
T2V 延时分布:
画面比例 | 宽 | 高 | 帧率 | step | 并行 | 视频时长 | 优化 | T2V视频生成耗时(单位:s) | 公式计算的tokens |
---|---|---|---|---|---|---|---|---|---|
16:9 | 1280 | 720 | 16 | 50 | SP8 | 5秒 | 业内 SOTA | 507 | 75600 |
16:9 | 1280 | 720 | 16 | 50 | SP8 | 5秒 | veFuser | 200 | 75600 |
16:9 | 832 | 480 | 16 | 50 | SP8 | 5秒 | 业内 SOTA | 183 | 32760 |
16:9 | 832 | 480 | 16 | 50 | SP8 | 5秒 | veFuser | 73 | 32760 |
在线服务启动好I2V或者T2V对应的环境,获得调用指南里公网访问的地址,不需要开启token验证
本测试在端到端的数据会直接在客户端日志中显示,得到的是客户端发送到接收视频完整的时间,具体每个节点实际运行的时间需要在在线服务的日志中查看每个节点启动该次测试的时间。
运行下面的脚本(黄色字体为需要根据实际修改的部分)
修改SERVICE_URL为你的实际调用地址
修改输入图片(I2V时用到)、输出视频、Prompt的文件地址
修改num_inference_steps和你的视频****生成方法一致:
如果是I2V,为40;如果是T2V,为50
如果是720P视频,height为1280,width为720;
如果是480P视频,height为832,width为480
your_script_folder/ ├── prompts.txt ← 每行一个 prompt ├── your_script.sh ← 你提供的 bash 脚本 ├── ../pictures/ ← 图片目录 │ ├── image1.png │ ├── image2.png │ └── ... ├── ../output1/ ← 输出视频或 JSON 的目录 │ └── video_2.mp4 / response_2.json
命令 | 作用 |
---|---|
curl | 向远程 API 发起 POST 请求 |
base64 | 将图片转为 base64 字符串 |
jq | 验证 JSON 格式是否有效 |
sed, awk, tr, grep | 标准的文本处理工具 |
mktemp | 创建临时目录用于中转数据 |
#!/bin/bash set -e # 服务和接口配置 SERVICE_URL="你的调用地址" ENDPOINT="/api/v1/generate" # 输入图片和输出视频的文件夹(请根据实际情况修改) INPUT_FOLDER="../pictures" # 存放图片的文件夹(要求图片命名为 image1.png, image2.png, …) OUTPUT_FOLDER="../output" # 存放生成视频的文件夹 # prompts 文件,每行对应一张图片的 prompt PROMPTS_FILE="./prompts.txt" if [ ! -f "$PROMPTS_FILE" ]; then echo "[ERROR] 找不到 prompts 文件:$PROMPTS_FILE" exit 1 fi # 确保输出文件夹存在 mkdir -p "$OUTPUT_FOLDER" # 指定需要处理的行号(index) INDEX=1 # 提取第 INDEX 行的 prompt prompt=$(sed -n "${INDEX}p" "$PROMPTS_FILE") if [ -z "$prompt" ]; then echo "[ERROR] 没有找到第 $INDEX 行的 prompt" exit 1 fi IMAGE_PATH="${INPUT_FOLDER}/image${INDEX}.png" if [ ! -f "$IMAGE_PATH" ]; then echo "[ERROR] 找不到图片文件:$IMAGE_PATH" exit 1 fi echo "正在处理第 $INDEX 张图片: $IMAGE_PATH" echo "当前 prompt: $prompt" # 创建临时目录 TMP_DIR=$(mktemp -d) trap 'rm -rf "$TMP_DIR"' EXIT # 将图片转换为 Base64(使用 -i 指定输入文件,并去掉换行符) base64 -i "$IMAGE_PATH" | tr -d '\n' > "$TMP_DIR/image.base64" # 生成 JSON payload,将 prompt 动态写入 cat <<EOF > "$TMP_DIR/payload.json" { "image": "$(cat "$TMP_DIR/image.base64")", "prompt": "$prompt", "num_inference_steps": 40, "height": 832, "width": 480, "num_frames": 81, "seed": 100000, "shift": 3.0, "sample_solver": "unipc", "guide_scale": 5.0, "return_type": "bytes", "negative_prompt": "色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走" } EOF # 验证 JSON 格式是否有效 if ! jq empty "$TMP_DIR/payload.json" &> /dev/null; then echo "[ERROR] 第 $INDEX 张图片生成的 JSON 无效" >&2 rm -rf "$TMP_DIR" exit 1 fi HEADER_FILE="$TMP_DIR/headers.txt" RESPONSE_FILE="$TMP_DIR/response.bin" echo "[INFO] 正在发送请求..." curl -s -X POST \ -H "Content-Type: application/json" \ --data-binary @"$TMP_DIR/payload.json" \ "${SERVICE_URL}${ENDPOINT}" \ -w '\nResponse Time: %{time_total}s\n' \ -D "$HEADER_FILE" \ --output "$RESPONSE_FILE" HTTP_STATUS=$(awk '/^HTTP/{status=$2} END{print status}' "$HEADER_FILE") if [ "$HTTP_STATUS" -ne 200 ]; then echo "[ERROR] 第 $INDEX 张图片请求失败,状态码: $HTTP_STATUS" >&2 rm -rf "$TMP_DIR" exit 1 fi CONTENT_TYPE=$(grep -i 'Content-Type:' "$HEADER_FILE" | head -n1 | sed 's/^Content-Type: //i; s/;.*//' | tr -d '\r') if [[ "$CONTENT_TYPE" == video/* ]]; then echo "[INFO] 第 $INDEX 张图片生成了视频响应。" FILE_EXT=$(echo "$CONTENT_TYPE" | cut -d'/' -f2) OUTPUT_FILE="${OUTPUT_FOLDER}/video_${INDEX}.${FILE_EXT:-mp4}" mv "$RESPONSE_FILE" "$OUTPUT_FILE" if [ ! -s "$OUTPUT_FILE" ]; then echo "[ERROR] 视频文件 $OUTPUT_FILE 为空,请检查生成过程。" exit 1 fi echo "视频保存为:$OUTPUT_FILE" elif [[ "$CONTENT_TYPE" == application/json* ]]; then echo "[INFO] 第 $INDEX 张图片返回的是 JSON 响应。" mv "$RESPONSE_FILE" "${OUTPUT_FOLDER}/response_${INDEX}.json" echo "JSON 响应保存为:${OUTPUT_FOLDER}/response_${INDEX}.json" else echo "[ERROR] 第 $INDEX 张图片返回的内容类型异常: $CONTENT_TYPE" >&2 file "$RESPONSE_FILE" rm -rf "$TMP_DIR" exit 1 fi # 清理临时目录 rm -rf "$TMP_DIR" echo "处理完毕!" exit 0
分辨率 | 节点数 | Step | 并行方式 | RDMA | 端到端生成时间(s) | DiT时间(s) | VAE时间(s) | DiT/VAE卡数比(QPS配平) | DiT Serving机器数 | 每月生成视频数 | DiT加速比 | 端到端加速比 | 端到端加速比 | 相对性价比 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
480P | 1 | 50 | 8U | Yes | 53.79 | 47.29 | 5 | 75.664 | 9 | 493296.6801 | 100.00% | 100.00% | 160.59% | 60.42% |
480P | 2 | 50 | 2C8U | Yes | 30.36 | 23.86 | 5 | 76.352 | 9 | 488851.6345 | 198.20% | 177.17% | 284.52% | 53.52% |
480P | 2 | 50 | 8U2R | Yes | 34.26 | 27.76 | 5 | 88.832 | 11 | 513544.6686 | 170.35% | 157.01% | 252.13% | 47.43% |
480P | 2 | 50 | 4U4R | Yes | 41.99 | 35.49 | 5 | 113.568 | 14 | 511242.6036 | 133.25% | 128.10% | 205.72% | 38.70% |
480P | 2 | 50 | 2U8R | Yes | 68.33 | 61.83 | 5 | 197.856 | 24 | 503056.7686 | 76.48% | 78.72% | 126.42% | 23.78% |
480P | 4 | 50 | 2C8U2R | Yes | 20.64 | 14.14 | 5 | 90.496 | 11 | 504101.8388 | 334.44% | 260.61% | 418.51% | 39.36% |
720P | 1 | 50 | 8U | Yes | 169.72 | 153.72 | 11 | 111.7963636 | 13 | 219203.7471 | 100.00% | 100.00% | 142.18% | 53.49% |
720P | 2 | 50 | 2C8U | Yes | 92.25 | 76.25 | 11 | 110.9090909 | 13 | 220957.377 | 201.60% | 183.98% | 261.58% | 49.21% |
720P | 2 | 50 | 8U2R | Yes | 110.76 | 94.76 | 11 | 137.8327273 | 17 | 232503.1659 | 162.22% | 153.23% | 217.87% | 40.98% |
720P | 2 | 50 | 4U4R | Yes | 110.73 | 94.73 | 11 | 137.7890909 | 17 | 232576.7972 | 162.27% | 153.27% | 217.93% | 40.99% |
720P | 2 | 50 | 2U8R | Yes | 137.13 | 121.13 | 11 | 176.1890909 | 22 | 235383.4723 | 126.90% | 123.77% | 175.97% | 33.10% |
720P | 4 | 50 | 2U8R | Yes | 56.42 | 40.42 | 11 | 117.5854545 | 14 | 224443.3449 | 380.31% | 300.82% | 427.70% | 40.23% |