从“省着点花”到“精确到每token成本”——我在云账单里翻到的秘密

30秒速览

  • GPU按需实例跑LLM推理,每千token成本比OpenAI贵了7倍,必须上连续批处理和Spot混合才能打平
  • MIG切分GPU听着美好,实际内存带宽争抢会让延迟飙到不可接受,用时间切片更稳
  • 自建K8s集群省下的服务费根本不够填人力坑,EKS加Karpenter才是理性人的选择
  • 存储和网络层面的“量化”——差分更新和gzip压缩——省的钱不比INT8少
  • 别再用CPU利用率做AI服务的扩缩容标准了,KEDA加Prometheus才是正解

从“省着点花”到“精确到每token成本”——我在云账单里翻到的秘密

上个月我收到一张云账单,数字是上上个月的两倍多。财务总监直接把邮件转发给我,只写了三个字:“解释下。”那个项目是一个日活50万左右的视频内容推荐平台,我们给用户推送的每条视频摘要都要过一个7B的LLM做润色和高亮片段提取。推理服务跑在AWS的g5.2xlarge上(一块A10G),每天大概350万次请求。我天真地以为只要控制好实例数量、设好自动关机脚本就算成本优化到位了,结果现实狠狠地给我上了一课。

我翻了好几天Cost Explorer,发现罪魁祸首根本不是机器数量,而是我们根本没有理解“AI负载”和传统微服务负载的本质区别。传统的Web服务、API网关,CPU内存够用、请求延迟在几十毫秒之内就能搞定,按请求数估算容量也比较准。但GPU推理不一样:一次LLM生成可能耗时1到6秒不等,而且显存占用、批处理效率、KV cache复用率这些东西比CPU利用率更影响成本。更坑的是,AWS的GPU实例计费是按秒起步,哪怕你用了一秒钟,它也算一分钟。我当时跑的都是单请求同步处理,GPU的SM利用率经常在30%以下,大量时间在等下一个请求到来——妥妥地给云厂商送钱。

我花了两个通宵做了一个“每有效token成本”的分析模型。把我们每天的实际推理请求、生成的token数量、实例运行时长和费用拉出来,算出了一个惊人的数字:每生成1000个有效token(只统计最终返回给用户的,不含prompt)平均成本是$0.042。对比OpenAI的GPT-4o mini价格(当时每百万输出token $0.6),我们自建推理居然贵了快7倍!这还不算运维人力成本。那一刻我才明白,自建推理省钱就是个幻觉,除非你把底层基础设施的利用率拉到极致。

我开始把所有费用按维度拆解:

费用项 占比 优化空间
按需GPU实例 62% 引入Spot + 预留实例
数据传输(跨AZ/出站) 13% 压缩、内网化
EBS卷 & 快照 9% 合并、删除孤立卷
负载均衡+网络 8% 换轻量级代理
监控日志存储 5% 采样、过期策略
其他(IP、支持费) 3% 几乎动不了

GPU实例的钱是大头,所以我先从这里动刀。但优化不能只用预留实例省钱那么简单,AI负载的弹性要求极高:晚高峰和凌晨的QPS能差8-10倍。预留实例买多了浪费,买少了扛不住。这时候必须把动态资源调度和请求排队做得足够精细。于是我做了几件事:

  • 把推理服务从直连GPU的Flask进程改成基于Triton Inference Server的部署,利用它的dynamic batching和model concurrency。
  • 引入请求优先级队列,非实时请求(如离线分析、A/B测试)可以用更低优先级的Spot实例处理。
  • 自己写了一个成本跟踪器,每次推理结束后上报本次消耗的GPU计算时长,然后乘以我们实际的实例单价,算出单次成本。这样业务方可以拿着成本数据去判断某个功能是不是“配得上”这么贵的推理。

下面是我写的成本跟踪装饰器,集成在Triton的Python backend里:

import time
import threading
from dataclasses import dataclass

# 全局实例单价,从环境变量注入,精确到秒
GPU_INSTANCE_COST_PER_SEC = float(os.getenv("GPU_COST_PER_SEC", "0.00122"))  # g5.2xlarge按需1.22美元/小时

@dataclass
class CostRecord:
    request_id: str
    gpu_time_ms: float
    cost: float

class CostTracker:
    def __init__(self):
        self._lock = threading.Lock()
        self.records = []

    def add(self, record: CostRecord):
        with self._lock:
            self.records.append(record)

    def flush_and_report(self):
        # 每5秒批量发送到监控后端,减少网络开销
        pass

tracker = CostTracker()

def track_gpu_cost(instance_cost_per_sec=GPU_INSTANCE_COST_PER_SEC):
    """
    装饰器:计算GPU kernel执行时长,然后换算成美元。
    注意:这里只统计CUDA kernel时间,不包含CPU预处理,
    因为我们只关心GPU这台昂贵机器的真实工作时间。
    """
    def decorator(func):
        def wrapper(*args, **kwargs):
            start_event = torch.cuda.Event(enable_timing=True)
            end_event = torch.cuda.Event(enable_timing=True)
            # 确保前面没有未完成的kernel干扰时间测量
            torch.cuda.synchronize()
            start_event.record()
            result = func(*args, **kwargs)
            end_event.record()
            torch.cuda.synchronize()  # 等待kernel执行完
            elapsed_ms = start_event.elapsed_time(end_event)  # 毫秒
            cost = (elapsed_ms / 1000) * instance_cost_per_sec
            # 记录但不阻塞
            tracker.add(CostRecord(request_id=kwargs.get("request_id", ""),
                                   gpu_time_ms=elapsed_ms, cost=cost))
            return result
        return wrapper
    return decorator

# 在Triton Python model execute里使用
class TritonPythonModel:
    @track_gpu_cost()
    def _run_inference(self, input_tensor):
        # 实际模型调用
        return self.model.generate(input_tensor)

    def execute(self, requests):
        # ...准备输入
        output = self._run_inference(input_tensor, request_id=requests[0].request_id())
        # ...返回响应

有了这个数据,我就可以准确告诉产品经理:“你那个每次强制生成三句摘要的需求,平均一次消耗0.002秒GPU时间,折合0.0000024美元,但每天600万次,一天就是14.4美元,一个月就是432美元。”这种精确到几分钱的话术可比“我们的GPU推理太贵了”有力得多,财务和业务两边都买账。

这一步只解决了成本可视化,还不够。下一阶段我直接把GPU实例的计费模式玩出了花。

我把Spot实例当长期饭票用,差点被中断率教做人

Spot实例(AWS的叫法,GCP叫Preemptible VM,Azure叫Spot VMs)的折扣最高能到90%,对我们这种弹性AI推理服务简直是天然的诱惑。我们最初用按需实例跑一个A10G,时薪$1.22。如果用Spot,价格经常能到$0.35以下。一算账,如果能把80%的负载迁移到Spot上,年成本能省出一辆Model Y。

于是我兴冲冲地在Auto Scaling Group里加了Spot容量,把按需实例的比例压到20%。刚上线那两天一切正常,第三天中午我正在吃饭,告警突然炸了:推理延迟从P99 1.2s飙升到8s,错误率5%。登陆上去一看,10台Spot实例在2分钟内被回收了7台,新的Spot请求因为当时可用区容量不足一直pending。Triton的服务端队列瞬间堆积了几万个请求,连带着几个按需实例的显存被KV cache撑爆导致OOM重启。整个推荐流直接掉了18分钟,用户看到的都是“暂时无法生成摘要”的占位图。

那次事故给我上了深刻的一课:Spot中断不只是“可能会丢一台机器”,它可能引起雪崩。我花了两周时间重写了调度层,做了下面几项硬核改造:

  • 基于中断预告的优雅退出。 AWS Spot实例在被回收前两分钟会通过metadata服务发出中断通知,我可以提前把模型从Triton的模型列表里unload、拒绝新请求、等待现有请求处理完再关机。但两分钟对LLM长生成可能不够,所以我把max_tokens限制在512以内,保证最坏情况下也能在30s内完成。
  • 异构节点池+反亲和调度。 不用单一的Spot容量池,而是混合g5.2xlarge (A10G)、g5.4xlarge (单A10G但可用区不同)、甚至g4dn.xlarge (T4) 专门跑轻量级的摘要打分而非生成。Kubernetes里通过nodeSelector和topologySpreadConstraints让Pod分散在不同可用区,避免被一锅端。
  • 请求缓冲与重试透明化。 在Triton前面加了一个Envoy代理,做了请求级重试和指数退避。当某个后端返回UNAVAILABLE时,Envoy自动换一个实例重试,应用层无感知。

下面是Kubernetes部署中Spot实例的Pod配置片段,注意我用的lifecycle preStop 和 terminationGracePeriodSeconds:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: triton-llm-spot
spec:
  replicas: 4
  selector:
    matchLabels:
      app: triton-llm
  template:
    metadata:
      labels:
        app: triton-llm
        spot: "true"
    spec:
      # 只调度到带有spot标签的节点
      nodeSelector:
        lifecycle: spot
      # 尽量分散在不同的AZ
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: triton-llm
      terminationGracePeriodSeconds: 90  # 给两分钟通知留出充足时间
      containers:
        - name: triton
          image: nvcr.io/nvidia/tritonserver:24.01-py3
          args: ["tritonserver", "--model-repository=/models", "--model-control-mode=explicit"]
          lifecycle:
            preStop:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - |
                    echo "Spot interruption, draining..."
                    # 从Triton里卸载模型,拒绝新请求
                    curl -X POST localhost:8000/v2/repository/models/llm_7b/unload
                    # 等待现有请求处理完毕,最多60秒
                    sleep 60
          env:
            - name: CUDA_VISIBLE_DEVICES
              value: "0"
          resources:
            limits:
              nvidia.com/gpu: 1

中断的处理逻辑还需要在应用层配合,我在Triton的Python backend里加了一个健康检查端点,当发现即将中断的信号时(从文件标志位读取),立即开始拒绝新推理请求并返回特定状态码,告诉Envoy“我不接客了,找别人去”。这个标志位由一个小型sidecar容器监控Spot中断通知后写入共享emptyDir。

经过这么一通折腾,我们的Spot可用率稳定在了99.7%。但这里有个成本上的“小九九”:由于需要预留缓冲,我们不能真把Spot比例拉到95%,必须留差不多35%的按需或预留实例兜底。我最终找到了一个黄金比例:65% Spot + 25% 预留实例(一年期,便宜40%)+ 10% 按需动态浮动。整体GPU成本降到了$0.52/小时,是原来纯按需的42%。

别被厂商宣传骗了,MIG分区的性能损耗能到15%

A100和H100上的MIG(Multi-Instance GPU)技术听起来很美好——把一张80GB的A100切成7个独立的GPU实例,每个10GB显存,跑多个小模型或者多租户推理。我当时想的是把一张A100切成四块5GB的分区,分别跑4个不同的模型(一个图像分类、一个文本向量化、两个LLM),这样一台机器当四台用,省到飞起。

现实是,切完分区之后,第一个跑benchmark的同事就来找我了:“图像分类的延迟从12ms涨到了18ms,而且抖动非常厉害。”我一开始以为是驱动bug,反复重装、调整MIG配置,问题依旧。后来用nvidia-smi和NVIDIA Nsight分析,发现MIG分区虽然显存和计算单元物理隔离,但L2缓存、内存带宽、DRAM控制器这些共享资源根本不可能完全隔离。当一个分区的模型在做大batch的KV cache存取时,另一个分区的计算核会明显受到带宽抢占的影响。

我测试了三种配置:

配置 模型A延迟 (P50) 模型B延迟 总吞吐提升
单进程独占A100 12ms baseline
MIG 2g.10gb 两个分区 13~14ms 13ms 1.85x
MIG 3g.20gb 两个分区 15ms 15ms 1.6x
MIG 1g.5gb 四个分区 18ms (抖动严重) 20ms 2.2x 但不稳定

性能损耗最小的是2g配置(大约8-10%),但显存只有10GB,装不下7B模型。3g.20gb勉强装下,但延迟已经开始漂移。4个1g分区完全没法跑LLM,只能跑轻量模型,而且干扰严重。最终我放弃了MIG方案,转向了GPU时间切片(Time-Slicing)配合CUDA MPS(Multi-Process Service),虽然隔离性差一些,但延迟更可控。

下面是我在Kubernetes里配置时间切片的device plugin,让多Pod共享GPU:

# 安装 nvidia-device-plugin 带 time-slicing 配置
# 在 daemonset args 里加入如下配置文件,mount进容器
apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
data:
  time-slicing-config.yaml: |
    version: v1
    sharing:
      timeSlicing:
        renameByDefault: false
        resources:
          - name: nvidia.com/gpu
            replicas: 4  # 每个物理GPU被分成4个逻辑GPU

这样,一个Pod请求nvidia.com/gpu: 1时,实际上只占用了1/4的GPU时间片。但要注意,时间切片不隔离显存,如果两个Pod同时加载了7B模型,显存直接爆掉。所以我规定同节点的Pod必须加载同一个模型的不同副本,通过模型预热镜像把模型权重加载到共享内存,再由各进程mmap,避免重复占用显存。这又引出了另一个优化:模型存储和加载的IO。

时间切片方案最终让单张A100同时服务3个LLM推理进程(每个4GB显存占用,共享模型权重后总占用约18GB),整体吞吐量提升了2.6倍,P99延迟增加不到20%,比MIG实用得多。MIG只适合那种能严格划分内存、对延迟波动容忍度极高的批处理任务,不适合在线LLM服务。

Triton动态批处理:延迟和吞吐的刀尖上跳舞

动态批处理(dynamic batching)是推理服务优化的核心手段,但坑也多到可以写一本书。我们的LLM推理服务在单请求模式下,GPU的计算单元利用率(SM utilization)常年在30%徘徊,因为token生成是一步步出的,单次矩阵乘的维度很小,根本喂不饱GPU。只有把多个请求拼成batch,才能把计算密度提上来。

Triton提供了两种批处理方式:默认的dynamic batcher和inflight batching。一开始我用默认的,设了max_batch_size=8,delay=100微秒。结果发现小batch还行,batch一大,显存分配速度跟不上,反而增加延迟。后来我把整个架构改了,用continuous batching(也就是inflight batcher),让请求在迭代层面混合:任何时候有token生成结束的请求退出,新请求立刻补上。这样GPU的计算队列永远不空。

下面是Triton的模型配置文件,启用inflight batching:

# config.pbtxt
name: "llama_7b"
backend: "python"
max_batch_size: 16

parameters: [
  {
    key: "enable_inflight_batching",
    value: { string_value: "true" }
  }
]

instance_group: [
  {
    count: 1,
    kind: KIND_GPU,
    gpus: [ 0 ]
  }
]

# 调度策略设为batch和stream结合
dynamic_batching {
  max_queue_delay_microseconds: 50
}

但光配Triton还不够,Python backend里的代码必须支持从batch中提取单个请求的token完成状态,并适时插入新请求。我用的是vLLM的引擎做底层推理,它原生支持continuous batching。当我把这个配置上线后,单个A10G的吞吐从350 tokens/s飙到了1200 tokens/s,成本直接打三折。

代价是延迟的P99从1.2s变成了2.8s。因为continuous batching为了等更多请求拼batch,单个请求可能会被拖慢。但我们的业务对摘要生成的延迟要求是小于5秒,2.8秒完全可以接受。这就是trade-off——你愿意为了吞吐量牺牲一点延迟吗?我和业务方签了一个SLA:只要90%的请求在3秒内完成,成本减了多少我就给他们展示,最后他们欣然接受了。

这里有个经验教训:不要自己折腾batching逻辑,除非你是搞CUDA kernel开发的。直接用vLLM、TensorRT-LLM这种成熟的连续批处理引擎,让它们和Triton集成。我早期手写了一个Python的batching队列,靠asyncio和信号量,结果死锁、内存泄漏、请求饥饿轮着来,连续三周凌晨两点被叫起来。

自建Kubernetes集群的200天:一个运维黑洞的诞生

2024年初,我所在的团队曾经试图完全自建Kubernetes集群来跑AI推理——觉得托管服务(EKS, GKE)太贵,而且定制化受限。我们用terraform在三台裸金属服务器上搭了K8s,配上NVIDIA device plugin、Prometheus、Grafana、Istio。最开始感觉掌握了一切,但很快就被现实暴击。

首先是GPU驱动的安装问题。裸金属的NVIDIA驱动需要和内核版本匹配,一旦自动升级了内核,驱动就挂掉,然后所有的GPU Pod全部CrashLoopBackOff。我们甚至发生过内核恐慌直接导致整台机器重启的事故,因为某个CUDA版本和内核模块冲突。托管服务的节点组会自动处理这些,但自建就得自己写systemd service、hook住apt upgrade。维护成本直线上升。

其次,网络CNI插件Calico的BGP路由在大流量下出现了丢包,导致推理服务的gRPC流断连。我们不得不切到Cilium,又是一通折腾。存储方面,用Longhorn做分布式块存储跑模型仓库,结果IOPS不到100,加载一个13B模型要15分钟,严重影响Pod的启动时间。后来换成节点本地NVMe SSD + rsync同步模型文件,才解决问题。

最终压垮我们的是升级Kubernetes版本。从1.28升1.29,好几个API deprecation,一堆自定义controller不兼容,需要重写。团队里只有两个SRE能搞定,但他们还要管公司的其他基础设施。连续两个版本的升级都花了接近两周的深夜时间,最后我拍板:放弃自建,回滚到EKS,哪怕多付点服务费。

迁移到EKS后,我们计算了一下,虽然EKS集群管理费每月$73,加上节点组的运维少了很多人工投入。我们两个SRE之前每月至少投入80小时在维护K8s上,换算成人力成本远超EKS费用。自建集群的200天经历让我彻底明白:除非团队有专门的K8s平台组,而且业务量极大、极致压榨成本,否则托管服务就是最优解。

但托管不是“撒手不管”。我仍然在EKS上做了很多优化,比如用Karpenter代替Cluster Autoscaler,让节点弹性伸缩快了一倍;使用自定义的AMI预先缓存模型权重和Triton server镜像,缩短节点启动时间到2分钟以内。这些优化代码在下一节具体说。

量化不只有INT8,存储和网络也要“瘦身”

一说到AI负载的优化,所有人第一反应是模型量化:FP16到INT8,甚至INT4。但我在实践中发现,基础设施层面的“量化”——即减少数据传输和存储的体积——带来的成本节省不比模型量化小。

先看存储。我们的模型仓库里有十几个不同版本的7B模型、embedding模型、重排序模型,每个动辄十几GB。每次发版、回滚要在节点间拷贝,网络流量和EBS快照费用都很高。我引入了Delta update机制:不用全量传输模型文件,而是通过rsync或zstandard差分只传变更的权重分片。同时,在模型存储上使用了写时复制(copy-on-write)的文件系统,配合btrfs的reflink,让不同版本的模型共享相同的基础权重,仅保存差异部分。这让我们模型仓库的EBS使用量从420GB降到了110GB,快照成本降了70%。

网络层,LLM推理服务返回的JSON响应中经常有大段的文本内容,客户端再解析展示。我们原先没用压缩,因为认为CPU压缩会增加延迟。后来做了压测:在响应体大于2KB时启用gzip压缩,CPU多消耗0.3ms,但网络传输数据量减少60%以上(文本压缩比高)。这不仅能省出站流量费,还能降低客户端加载时间,特别是在移动网络下。我们直接在Envoy代理上开启了compress过滤器,对content-type为application/json的响应自动gzip,效果立竿见影。出站流量费用从每天$38降到了$14。

然后是模型推理的提示词(prompt)压缩。很多请求的prompt重复度极高,比如系统指令、上下文前缀。我们自己在Triton前端写了一个缓存层,根据prompt的哈希值缓存相应的KV cache(实际上是vLLM自动的prefix caching)。但即使这样,原始提示词的传输也消耗网络。我在客户端SDK里加入了prompt模板引用,只传变化的部分,服务端拼装完整prompt。比如一个请求只需要传{“template”: “summary_v2”, “params”: {“title”: “xxx”, “content”: “yyy”}},服务端内部把内容填入大段的system prompt模板。这又减少了约40%的请求体积,进一步降低出站和入站流量费。

下面是我们的Envoy配置,启用gzip和请求体大小限制:

# envoy.yaml 片段
http_filters:
- name: envoy.filters.http.compressor
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor
    compressor_library:
      name: text_optimized
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip
        memory_level: 5
        compression_level: BEST_SPEED  # 低CPU开销,高压缩率
        compression_strategy: RLE
    request_direction_config: {}
    response_direction_config:
      common_config:
        min_content_length: 200  # 小于200字节不压缩
        content_type:
          - "application/json"

存储和网络的“瘦身”加起来,一个月省了约$1200,几乎相当于免费多跑了一个模型实例。这些优化不像模型量化那样性感,但都是实实在在地把成本数字往下砸。

当CPU使用率不再是扩缩容的唯一标准:AI负载下的HPA变形记

传统的Kubernetes水平自动伸缩(HPA)看CPU和内存利用率,这招对AI推理服务几乎完全失灵。GPU推理的CPU使用率不高(因为大部分活儿在GPU上),内存占用也基本恒定(模型加载后就稳定了)。真正能反映负载压力的是请求排队深度、GPU SM利用率和推理延迟。

一开始我们试图用HPA的external metrics,通过Prometheus查询请求队列长度来自动伸缩。配置如下:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: triton-llm-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: triton-llm
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: External
    external:
      metric:
        name: triton_pending_request_count
      target:
        type: AverageValue
        averageValue: "5"  # 平均每个Pod等待请求数超过5就扩容

但是队列长度是个滞后指标,扩容要等新Pod启动、模型加载完成,这个过程需要3-5分钟。流量激增的时候,往往来不及。我们改用了KEDA(Kubernetes Event-driven Autoscaling),它支持更灵活的触发器,并且可以把伸缩决策前置。

我用KEDA的Prometheus scaler监控Triton暴露的`nv_inference_queue_duration_us`指标,当排队时间中位数超过500ms就触发扩容。更重要的是,KEDA支持缩容到零,凌晨低峰期我们直接把推理服务缩到零,配合请求到来时通过冷启动触发器(比如HTTP请求到达时由网关唤醒),实现Serverless式的GPU推理。虽然冷启动需要约2分钟,但凌晨的请求极少,延迟多一点完全可以接受。

但唤醒冷启动对LLM是灾难,因为模型加载太慢。于是我们做了一个trick:不缩容到零Pod,而是保留一个最小实例(minReplicas=1),但这个实例在低负载时自动切换到一个更小更便宜的模型版本,比如从7B切换到1B,或者切换到CPU推理(用llama.cpp跑Q4量化的7B模型),虽然慢一点但凌晨请求量极少,能扛住。等流量上来,KEDA触发扩容,新Pod带7B模型上线,然后旧的迷你Pod被替换掉。这个切换通过修改Service的selector和一个蓝绿Deployment实现。

下面是我用KEDA定义的ScaledObject,配置冷却时间避免频繁伸缩抖动:

apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: triton-llm-scaler
spec:
  scaleTargetRef:
    name: triton-llm
  minReplicaCount: 1  # 至少留一个迷你模型
  maxReplicaCount: 8
  cooldownPeriod: 300  # 缩容冷却5分钟
  triggers:
  - type: prometheus
    metadata:
      serverAddress: http://prometheus-server.monitoring.svc:9090
      metricName: triton_queue_duration_p50
      threshold: '500000'  # 500ms,单位微秒
      query: |
        avg(rate(nv_inference_queue_duration_us{model="llama_7b", quantile="0.5"}[1m]))

为了支持凌晨的迷你模型切换,我还写了一个简单的operator,监听一个ConfigMap里的“mode”字段,动态调整Deployment的镜像和环境变量。这种做法虽然不优雅,但有效。省下了一台A10G实例凌晨7小时的花费,一个月又省了$250。

性能调优和成本控制在AI负载下是强耦合的,不能头痛医头。我从账单分析、Spot混合、动态批处理、MIG踩坑、存储网络量化、伸缩策略多个角度切入,最终把那个视频推荐平台的月GPU成本从$8,400压到了$3,100,P99延迟还略降了一点。最关键的是,现在我可以随时掏出单次推理的成本数据,告诉业务方他们花的每一分钱花在了哪里,这种透明让技术团队从“烧钱的黑洞”变成了成本优化的英雄。

本文由 AI 辅助生成,经人工审核后发布。内容由 林默 基于实战经验指导完成。

觉得有用?

林默

全栈开发者,写了8年代码,从jQuery时代一路写到AI Copilot。目前专注AI编程工具链的深度使用和评测,相信好的工具能让开发者事半功倍。喜欢用实际项目验证技术方案,不写没踩过坑的教程。

发表评论