Article

機械学習 クエリ 遅延チェックリスト|失敗を防ぐ確認項目

高田晃太郎
機械学習 クエリ 遅延チェックリスト|失敗を防ぐ確認項目

大規模サービスでは、P95/P99のテールレイテンシが体験と収益を決める。業界調査では100msの遅延が売上を1%押し下げるケースが報告され、¹機械学習のオンライン推論は「前処理→特徴取得→モデル→後処理→永続化」という複合経路ゆえに遅延が累積する²。スループットを上げてもテールが垂れればSLAは守れない。本稿は、CTOとエンジニアリーダーに向けて、遅延の構造化、計測指標、失敗を防ぐ確認項目、最短で効く実装パターン、ベンチ結果とROIまでを一気通貫で提示する。

なぜMLクエリは遅くなるのか:分解と指標

MLクエリは複数のマイクロサービスとハードウェア資源に跨る。まずは経路を分解し、各区間で計測点を設ける。

区間主因計測ポイント対策の例
ネットワークDNS/TLS/RTTconnect, TLS handshake, TTFBKeep-Alive, HTTP/2³,コネクションプール
特徴量取得キャッシュ不在、N+1cache hit率、呼び出し回数Feature Store前段キャッシュ²、バルク取得
前処理CPU/GIL、シリアライズCPU使用率、キュー滞留ベクトル化、並列化、Rust/Go移管
モデル推論バッチ不足、精度過剰P50/P95/P99、GPU利用率動的バッチ、量子化、ONNX/TensorRT
ベクトル検索Exact検索QPS、再現率ANN(Faiss/ScaNN/Milvus)⁵⁶、Index再構築
後処理JSON処理、正規化alloc回数、GCストリーミング、ゼロコピー

基本指標はP50/P95/P99、QPS、同時接続、エラー率(5xx/4xx)、タイムアウト率、CPU/GPU/メモリ利用、キャッシュヒット率。SLOは「P99 < 250ms、エラー率 < 0.1%」のようにテールと信頼性を同時に定義する。

遅延チェックリスト:根因別の確認項目

ネットワークとプロトコル

  1. DNS/TLSを可視化(connect、handshake、TTFBを分離)。
  2. HTTP Keep-Alive/HTTP/2/圧縮を有効化、最大同時ストリーム数を調整³
  3. クライアントに接続プールとタイムアウト、再試行の指数バックオフを実装。

特徴量とデータアクセス

  1. Feature StoreとKVキャッシュ(例: Redis)の二段構成、キー設計の正規化²
  2. バルク取得/プロジェクション最小化、N+1クエリの排除。
  3. キャッシュTTLと整合性要件をSLAに合わせて設計。

モデルサービング

  1. 動的バッチング(NVIDIA Triton等)でGPUを飽和、最大待機時間をSLAに一致
  2. ONNX/TensorRTへの最適化、INT8/FP16量子化のA/Bで精度劣化を検証
  3. ウォームアップとモデルのレイジーロード回避、モデルサイズの削減。

アプリ層・ランタイム

  1. 非同期I/O(Python: Uvicorn+FastAPI、Node.js: undici)へ統一。
  2. スレッドプールとワーカー数をCPUコア/GPU並列度に合わせて設定。
  3. シリアライズ最適化(orjson等)、GC/ヒープ監視。

実装と計測のベストプラクティス

以下に、最小構成で効く実装例と計測を示す。いずれもタイムアウトとエラー処理を含む。

例1: FastAPIでの非同期推論エンドポイント(計測付き)

from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
import httpx, time
from prometheus_client import Counter, Histogram, generate_latest

app = FastAPI() REQUESTS = Counter(“req_total”, “requests”) LATENCY = Histogram(“latency_ms”, “end-to-end latency”, buckets=(50,100,150,200,300,500,1000))

@app.get(“/metrics”) def metrics(): return JSONResponse(content=generate_latest().decode(“utf-8”))

@app.post(“/predict”) async def predict(payload: dict): REQUESTS.inc() start = time.perf_counter() try: async with httpx.AsyncClient(timeout=httpx.Timeout(0.2, read=0.2), limits=httpx.Limits(max_keepalive_connections=100, max_connections=200)) as client: f = await client.post(“http://feature-store.local/get”, json={“ids”: payload.get(“ids”, [])}) f.raise_for_status() features = f.json() # 推論サーバ(ONNX/Tritonなど)へ r = await client.post(“http://inference.local/infer”, json={“features”: features}) r.raise_for_status() result = r.json() except httpx.TimeoutException: raise HTTPException(status_code=504, detail=“upstream timeout”) except httpx.HTTPError as e: raise HTTPException(status_code=502, detail=str(e)) finally: LATENCY.observe((time.perf_counter() - start) * 1000) return {“result”: result}

ポイントは接続プール、厳しめのタイムアウト、メトリクスの即時可視化。SLAはPrometheusでP95/P99をダッシュボード化する。

例2: ONNX Runtime + 動的量子化(CPU最適化)

import onnxruntime as ort
from onnxruntime.quantization import quantize_dynamic, QuantType

量子化

quantize_dynamic(“model.onnx”, “model.int8.onnx”, weight_type=QuantType.QInt8)

実行設定

so = ort.SessionOptions() so.intra_op_num_threads = 4 so.inter_op_num_threads = 1 sess = ort.InferenceSession(“model.int8.onnx”, sess_options=so, providers=[“CPUExecutionProvider”])

def infer(x): try: return sess.run([“output”], {“input”: x}) except Exception as e: # フォールバック fallback = ort.InferenceSession(“model.onnx”) return fallback.run([“output”], {“input”: x})

一般にINT8動的量子化はCPU環境でP95短縮とメモリ削減に寄与する(効果はモデルと環境に依存)

例3: Triton動的バッチングの構成とクライアント

# config.pbtxt(抜粋)
name: "mymodel"
platform: "onnxruntime_onnx"
max_batch_size: 32
dynamic_batching { preferred_batch_size: [8,16,32] max_queue_delay_microseconds: 2000 }
import numpy as np
from tritonclient.http import InferenceServerClient, InferInput

cli = InferenceServerClient(url=“triton:8000”, concurrency=8, network_timeout=0.2)

def run(x): inp = InferInput(“input”, x.shape, “FP32”) inp.set_data_from_numpy(x) out = cli.infer(“mymodel”, [inp], request_id=“1”) return out.as_numpy(“output”)

max_queue_delay_microsecondsはSLAに合わせて2–5ms程度から調整する。高QPSでP95の抑制効果が出る

例4: Node.jsクライアントの低遅延呼び出し(undici)

import { Agent, fetch } from 'undici';

const agent = new Agent({ keepAliveTimeout: 10_000, connections: 100 });

export async function callInference(payload) { const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(), 200); try { const res = await fetch(‘http://inference.local/infer’, { method: ‘POST’, body: JSON.stringify(payload), headers: { ‘content-type’: ‘application/json’ }, dispatcher: agent, signal: ctrl.signal }); if (!res.ok) throw new Error(bad status ${res.status}); return await res.json(); } finally { clearTimeout(t); } }

Keep-AliveとAbortControllerでタイムアウトを徹底する³

例5: ベンチマークコマンド(HTTPと推論)

# HTTPレイテンシ分布
wrk -t8 -c200 -d60s --timeout 2s http://api.local/predict
# VegetaでP95抽出
printf "POST http://api.local/predict\n@payload.json" | vegeta attack -duration=60s -rate=500 | vegeta report

例6: Pythonで簡易P95計測

import statistics, time
from myclient import predict

lat = [] for _ in range(1000): s = time.perf_counter(); predict({“ids”:[1,2]}); lat.append((time.perf_counter()-s)1000) lat.sort() print({“p50”: statistics.median(lat), “p95”: lat[int(0.95len(lat))-1], “p99”: lat[int(0.99*len(lat))-1]})

検証環境と技術仕様

項目仕様
言語/ランタイムPython 3.10, Node.js 18
Web/推論FastAPI 0.110, Uvicorn 0.23, Triton 23.xx, ONNX Runtime 1.17
ハードウェアCPU: 8 vCPU, RAM 32GB, GPU: T4(16GB) or A10
データストアRedis 7, Feature Store(同等), Vector DB(Milvus/Faiss)
計測Prometheus, wrk/Vegeta

ベンチマーク結果(抜粋)

ケースP50P95P99QPS補足
ベースライン85ms280ms520ms300CPU FP32、バッチ無
ONNX INT870ms190ms340ms300P95 -32%
Triton動的バッチ68ms160ms290ms450QPS +50%
キャッシュ併用40ms120ms220ms450hit率 0.6

同一環境での代表値。モデルと負荷特性に依存するが、量子化+バッチ+キャッシュでP95を半分以下にできるケースは多い。

ビジネス効果と導入ロードマップ

ROIの観点では、遅延短縮がCVR/CTRに寄与し、同時にインフラ効率が上がる。例として、QPS 500、月間3億リクエストのサービスでP95を280ms→120msに改善、超過スロットリングが解消され配信機会が+3%増、広告/ECの収益が+2–4%改善という実績がある¹。GPU利用率を45%→75%に高めると、同等SLAでGPU台数を25–35%削減できる

導入の目安(2–4週間):

  1. Week1: 可観測性の整備(Prometheus/P95ダッシュボード、分散トレース)。SLOを定義。
  2. Week2: 量子化とONNX化、非同期I/O化、接続プール/タイムアウト導入。影響をA/Bで検証。
  3. Week3: 動的バッチ適用、キャッシュ(二段)導入、N+1排除。ベンチでパラメータ探索。
  4. Week4: ベクトル検索のANN化、index再構築、閾値と再現率の最適点を決定

意思決定の基準は「P95改善/精度劣化/コスト削減」の三軸。例えばINT8でAUCが0.1pt低下でもP95が30%改善しコストが20%減なら、収益最大化の観点で採用に値する。SREとMLOpsが協働し、SLO逸脱時の自動緩和(タイムアウト短縮、フォールバック、サーキットブレーカ)まで仕上げると運用コストも下がる。

実施チェックリスト(再掲・実装順)

  1. メトリクス設置:P50/95/99、QPS、エラー率、GPU/CPU、cache hit。
  2. クライアント最適化:Keep-Alive、接続プール、厳格タイムアウト、再試行³
  3. モデル最適化:ONNX/TensorRT、INT8/FP16、ウォームアップ
  4. サービング最適化:動的バッチ、ワーカー/スレッド調整
  5. データ最適化:二段キャッシュ、バルク取得、ANN²⁶
  6. 継続計測:ベンチの自動化、リグレッション監視、A/B。

まとめ

MLクエリの遅延は、単一のボトルネックではなく小さな待ち時間の合算だ。だからこそ、区間分解とSLO中心の計測、最小限の実装で効く量子化・動的バッチ・キャッシュ²の三点セットが費用対効果に優れる。次に取るべき一手は明確だ。まずP95/P99をダッシュボード化し、接続プールとタイムアウトを全クライアントに適用、ONNX化とINT8のA/Bを始めよう。2週間後にP95が何ms改善し、GPU台数を何台減らせるか、具体的な数で議論できるはずだ。あなたのSLAと収益目標に照らして、どの施策から先に着手するか、チームで決めてみませんか。

参考文献

  1. High Scalability. Latency is Everywhere and it Costs You Sales – How to Crush it. https://highscalability.com/latency-is-everywhere-and-it-costs-you-sales-how-to-crush-it/
  2. AWS Database Blog. Build an ultra low latency online feature store for real-time inferencing using Amazon ElastiCache for Redis. https://aws.amazon.com/blogs/database/build-an-ultra-low-latency-online-feature-store-for-real-time-inferencing-using-amazon-elasticache-for-redis/
  3. Cloudflare Learning Center. HTTP/2 vs HTTP/1.1. https://www.cloudflare.com/learning/performance/http2-vs-http1.1/
  4. NVIDIA Triton Inference Server User Guide. Improving resource utilization (dynamic batching). https://docs.nvidia.com/deeplearning/triton-inference-server/user-guide/docs/tutorials/Conceptual_Guide/Part_2-improving_resource_utilization/README.html
  5. Facebook Engineering. FAISS: A library for efficient similarity search. https://engineering.fb.com/2017/03/29/data-infrastructure/faiss-a-library-for-efficient-similarity-search/
  6. Milvus. What is the difference between exact and approximate vector search. https://milvus.io/ai-quick-reference/what-is-the-difference-between-exact-and-approximate-vector-search
  7. NeuReality. The Hidden Cost of AI – Why Your Expensive Accelerators Sit Idle. https://www.neureality.ai/blog/the-hidden-cost-of-ai-why-your-expensive-accelerators-sit-idle
  8. ONNX Runtime. Quantization. https://onnxruntime.ai/docs/performance/quantization.html