🙌

Cloudflare WorkersでJevを使ったら高速か検証してみた

に公開

結論

  • Cloudflare Workers上でJevのAPIを直接使うのが最速
  • Cloudflare AI Gateway経由で使う場合AI Gateway経由分遅くなる
  • クライアントが日本な限りレイテンシは遅くなってしまう

はじめに

Cloudflare AI Gatewayでみんな大好きJevが使えるようになったので使ってみました。

https://developers.cloudflare.com/ai/models/typesafe/jev/

Jevの特徴としては「とにかく高速」ということなんですが、Jevのサーバーがアメリカの西海岸(Oregon)にあるらしく、日本からアクセスするとその速度が生かしきれないという問題があります。

そこでCloudflare Workers経由でJevを使ったら、もしかして高速にJevにアクセスできるのでは?という淡い期待が浮かびました。ただCloudflare Workersはクライアントからアクセスができる一番近いエッジサーバーで動作するため、もしクライアントが日本の場合は、日本のエッジサーバーが選ばれ、Jevからのレイテンシはそこまで変わらない、という仮説もあります。

結論から言うとこの仮説は当たっており、クライアントが日本な限りエッジサーバーは日本になり、Jevへのアクセスは海を越える必要がありました。

と言うことで今回はCloudflare WorkersでJevを使って速度を実際に検証してみました。

今回検証したこと

「ブラウザ(React)からHonoのAPIを叩き、HonoがJevを呼ぶ」という、実際にアプリを作るときの形でレイテンシを測りました。同じHonoアプリをCloudflare WorkersとNode.js(ローカルのmacOS上で起動)の両方で動かし、同じベンチページから計測しています。

Chrome(日本)──same-origin fetch──> Hono /api/*
                                      ├─ /api/jev/direct    fetch(api.typesafe.ai)     ← Jev を直接叩く
                                      ├─ /api/jev/gateway   env.AI.run("typesafe/jev")  ← AI Gateway 経由(Workers のみ)
                                      └─ /api/ping          Jev を呼ばない(往復時間の基準)

比較する経路は3つです。

経路 何をしているか
Workers → Jev直接 Workerのfetchでapi.typesafe.aiを叩く。APIキーはWorkerのシークレット
Workers → AI Gateway → Jev env.AI.run("typesafe/jev", ...)。Cloudflareのネイティブモデルとして呼ぶのでJevのキー不要、統合課金
Node → Jev直接 同じHonoを@hono/node-serverで起動してfetch

検証コードは全て公開してますので参照して下さい。
https://github.com/henteko/jev-dev-cloudflare

測り方

  • ブラウザ側はfetchの前後でperformance.now()を取り、合計時間としました。
  • Hono側はServer-Timingヘッダで「Hono → Jevにかかった時間(以下upstream)」を返し、ブラウザのResource Timing APIで読み取っています。これで「ブラウザ ⇄ サーバー」と「サーバー ⇄ Jev」を分けて見られます。
  • リクエスト内容は固定で、サポートチケットの文章に対してchoice(カテゴリ分類)、noul(返金要求の有無)、score(緊急度4段階)の3問を同時に聞くものです。1リクエストあたり入力455トークン、出力77トークン。

送り方のパターン

実際のアプリでは呼び出し頻度によって接続の温まり方が変わるので、3パターン用意しました。

パターン 内容 何を見たいか
warm 1秒間隔で30回 接続が温まった状態の基準値
parallel 5本同時×5ラウンド 画面で複数の判定を同時に投げるケース
sporadic 30秒間隔で10回 たまにしか呼ばれない状態(接続やisolateが冷える)

経路はラウンドロビンで交互に実行して、時間帯の揺らぎが均等にかかるようにしています。

検証結果

合計時間のp50 / p95と、Hono → Jevのupstream p50です(単位ms)。p50は中央値、p95は95%のリクエストがこれ以内に収まる値のことです。

パターン Workers → Jev直接 Workers → AI Gateway → Jev Node → Jev直接
warm 207 / 393(upstream 192) 267 / 1004(upstream 235) 246 / 294(upstream 235)
parallel 218 / 311(upstream 196) 284 / 780(upstream 265) 297 / 555(upstream 282)
sporadic 315 / 629(upstream 290) 744 / 1463(upstream 652) 567 / 883(upstream 558)

ブラウザ ⇄ Honoの往復(/api/ping)はWorkersで約20msでした。つまり時間のほとんどはNRT(東京のCloudflare Workersサーバー)からOregonまでの往復とJev自体の処理時間です。Honoのパフォーマンス自体はほとんど影響していません。

Workers → Jev直接が全パターンで最速

warmで207ms。Jevの処理時間が90ms前後なので、往復100msほどでOregonまで行って帰ってきている計算です。parallelでもsporadicでも他の2経路より速く、今回の中では唯一すべてのパターンで優勝でした。

AI Gateway経由は条件によっては遅い

warmでは直接より40msほど遅いだけで、ログや統合課金が付くならメリットがありそうです。ただsporadicでは10回中9回が590〜760ms、最大1390msと、直接の2倍以上かかりました。30秒間隔が空くとGatewayの接続や状態も冷えてしまい、そこにGateway自体の処理が乗るようです。

リクエストの間隔が空く可能性があるAPIでGatewayを挟むのはレイテンシ的には不利、という結果でした。

Nodeは「接続の寿命」で負ける

warmではWorkers直接に40ms劣るだけですが、sporadicは10回すべてが510〜870msで、Workersの倍近くになりました。中身を見るとwarmの235msに対して約280msの上乗せで、これはOregonまでのTCP + TLSハンドシェイク(往復140ms × 2)にぴったり一致します。Node(undici)のkeep-aliveは既定4秒なので、30秒空くと毎回接続を張り直しているわけです。

parallelも面白くて、1ラウンド目だけ5本中4本が520〜570msでした。Node → JevはHTTP/1.1なので1接続に同時1リクエストしか流せず、温まっていた1本以外は新規に接続を張っています。Workers → Jevはh2で多重化されるため、1ラウンド目から150〜250msでした。

考察

なぜ差が出たのか

パターンごとに原因が違います。

パターン 差の主因 補足
warm ネットワーク経路 自宅回線 → Oregonは往復140ms、NRT → OregonはCloudflareのバックボーン経由で約100ms
parallel HTTP/1.1かh2か WorkersはJevとh2で多重化、Nodeは接続を本数分張る
sporadic 接続の寿命 Workersはcolo側の接続プールがisolateと独立に残る(10回中4回はwarmと同じ速さ)。Nodeは4秒で切れる

Node側の差はまだ改善の余地があります。keep-aliveを60秒以上に伸ばせばsporadicの張り直しは減り、undiciのallowH2を有効にすればparallelの初回コストも消えるはずです。ただしwarmの40msは経路差なので、自宅回線から測っている限り残ります。東京のデータセンターにあるVPSなら、Oregonへの往復は100ms前後になるので、この差はほぼ消えると思います。

もしクライアントが西海岸だったら

今回は日本からの計測ですが、Workersはクライアントに一番近い場所で動くので、西海岸のユーザーならSJCやLAXあたりで実行され、OregonのJevまでは往復10〜30msです。推定で120〜150msになり、海を越える分がまるごと消えます。設定は何も要りません。

一方、東京に置いたVPSだと西海岸のユーザーは海を2回越えることになり、350〜400ms見込みです。「どこからアクセスされても自動で近いところからJevに行く」のはWorkersの明確なメリットですね。

なお、日本のユーザー向けにVPSをJevの隣(Oregon)に置いても、クライアント → Oregonの往復140msは残るので235ms前後になり、東京VPSと変わりません。JevがOregonにある以上、日本からは誰かが太平洋を1往復する必要があって、それをCloudflareのバックボーンで済ませられる分だけWorkersが速い、という整理になります。

素直にJevが東京リージョンのサーバーを立てるのを待った方が良さそうですね。

Smart Placementは検証できず

今回のようなケースでは、Cloudflare WorkersのSmart Placementがもし試せたら、WorkersとJev間のレイテンシは速くなる可能性がありましたが、トラフィックを大量に用意する必要があり、実質的に試すことは難しかったです。

https://developers.cloudflare.com/pages/functions/smart-placement/

さいごに

とりあえずCloudflare Workersを使っておけば、クライアントがJevの近くにいればいるほど高速化できるので、使っておけば良さそうだなという身も蓋もない結論になりました。
AI Gateway経由にするかどうかは、多少速度を犠牲にしても、API Tokenの管理や請求管理の一括化ができるため、個人的にはおすすめです。カリカリに速度が必要な場合は直接の方がいいのは言わずもがなですね。

Discussion