Column

コラム

  • Gemini の Context caching でレイテン...

Gemini の Context caching でレイテンシはどれほど削減できるか

DataCurrent の金子です。Gemini の Context caching を検証をしてみました。

Gemini を利用して大量のドキュメントを処理したり、繰り返し同じようなコンテキスト情報を扱ったりする際、レスポンスの速度やコストは重要な課題です。Context caching は、これらの課題解決に役立ちます。

※ この記事は、2025 年 6 月時点の Google Cloud の公式ドキュメントを参考にしています。記事の内容は古くなっている可能性がありますので、最新の情報は公式ドキュメントをご確認ください。なお、Gemini 2.5 モデルでは入力が自動的にキャッシュされますが、本記事で検証しているユーザーが明示的に作成・管理する Context caching の挙動とは異なる可能性があるため、本記事の検証対象外としています。

Context caching とは?

Context caching は、Gemini の機能の一つで、モデルへのリクエストに含まれるコンテキスト情報をキャッシュ、一時保存することで、後続の同様のリクエストにおけるレイテンシ、応答時間の削減コスト効率の向上を目指すものです。

主なメリット:

  • レイテンシの削減: 一度コンテキストをキャッシュしてしまえば、次回以降のリクエストではその大きなコンテキストデータを再度送信する必要がなくなります。これにより、特に大きなデータを扱う場合に、モデルからの応答が速くなることが期待できます。
  • コスト効率の向上: 後述する料金体系により、キャッシュを効果的に利用することで、API 利用料金を抑えることができます。

仕組みの概要:

  1. ユーザーは、キャッシュしたいコンテンツ、テキスト、PDF、画像、動画など、Gemini がサポートする様々な形式を指定して、「キャッシュオブジェクト」を作成します。
  2. このキャッシュオブジェクトには、有効期限 TTL: Time To Live を設定できます。
  3. 以降の API リクエストで、このキャッシュオブジェクトを指定すると、モデルはキャッシュされた情報を効率的に利用して応答を生成します。
  4. 不要になったキャッシュは削除することも可能です。

料金について:

Context caching の利用料金は、主に以下の2つの要素で構成されます。

  • キャッシュストレージ料金: キャッシュされたデータの量(トークン数)と保存期間(時間)に基づいて課金されます。具体的には、100 万トークン時間あたり $1.00 です。
  • キャッシュされたトークンの処理料金:
    • キャッシュヒット時: キャッシュされたコンテキストが API リクエストで再利用された場合、キャッシュヒット、そのキャッシュされた入力トークンには割引料金が適用されます。例えば、キャッシュヒットした入力トークンは標準の入力トークン料金の 25% の料金、つまり 75% 割引で課金されます。
    • キャッシュミス時およびキャッシュ作成時: キャッシュが存在しない、または有効期限切れでコンテキストがキャッシュにない場合、キャッシュミス、あるいは新たにコンテキストをキャッシュに書き込む際には、通常の入力トークン料金が適用されます。

詳細な料金情報や最新の情報は、Vertex AI の料金ページでご確認ください。

Context cachingの効果を検証してみた

それでは、実際に Context caching がどの程度の効果をもたらすのか、具体的なタスクで検証してみましょう。

今回は、公開されている PDF ファイルを Gemini モデルに読み込ませ、その内容について要約を生成させるというタスクで、Context caching を使用した場合と使用しない場合の応答レイテンシを比較します。

今回の検証では、gemini-2.0-flash-001 モデルを使用します。Context caching の有無による応答レイテンシの違いを見るため、それぞれ 10 回リクエストを行い最初のトークンが返るまでの平均時間を比較しました。

実行したコードは以下の通りです。

# このスクリプトは、Gemini モデルを使用し、PDF ファイルをコンテキストとしてコンテキストキャッシュの有無によるレイテンシを比較します。
import os
import time
import uuid

from google import genai
from google.genai import types

# --- Vertex AI 利用のための環境変数設定 ---
# GOOGLE_CLOUD_PROJECT="your-gcp-project-id"
# GOOGLE_CLOUD_LOCATION="us-central1"
# GOOGLE_GENAI_USE_VERTEXAI=True
# ------------------------------------

MODEL_NAME = "gemini-2.0-flash-001"
NUM_ITERATIONS = 10
PDF_GCS_URI = "gs://cloud-samples-data/generative-ai/pdf/1706.03762v7.pdf"
PROMPT_PDF = "この PDF を読んでください。"
PROMPT_SUMMARY = "この PDF の主な貢献を3点で要約してください。"
SYS_INSTRUCTION = "あなたはリサーチアシスタントです。情報源の事実に忠実であれ。"
CACHE_TTL = "300s"
TEMPERATURE = 0.0
SEED = 1234

http_options = types.HttpOptions(api_version="v1")
client = genai.Client(http_options=http_options)


def measure_latency(contents: list[types.Content],
                    desc: str,
                    cache_name: str | None = None) -> float:
    """
    Args:
        contents: generate_contentに渡すコンテンツのリスト。
        desc: 測定の説明(例:「キャッシュなし」、「キャッシュあり」)。
        cache_name: 使用するキャッシュの名前(オプション)。

    Returns:
        測定された平均レイテンシ(ミリ秒単位)。
    """
    latencies = []
    print(f"\n--- {desc} レイテンシ測定 ---")

    generation_config_obj = types.GenerateContentConfig(
        temperature=TEMPERATURE,
        seed=SEED,
        cached_content=cache_name,
    )

    for i in range(NUM_ITERATIONS):
        start = time.perf_counter()

        stream = client.models.generate_content_stream(
            model=MODEL_NAME,
            contents=contents,
            config=generation_config_obj,
        )
        r = next(stream)
        latency = (time.perf_counter() - start) * 1000
        latencies.append(latency)

        cache_status_message = ""
        token_count = None
        if r.usage_metadata:
            token_count = r.usage_metadata.cached_content_token_count

        if token_count is not None and token_count > 0:
            cache_status_message = "(キャッシュヒット確認)"
        else:
            cache_status_message = "(キャッシュヒットなし)"

        print(
            f"イテレーション {i+1}/{NUM_ITERATIONS}: {latency:.2f} ms (最初のトークン) {cache_status_message}"
        )

    avg_latency = sum(latencies) / len(latencies) if latencies else float(
        'inf')
    if latencies:
        print(f"{desc} 平均: {avg_latency:.2f} ms ({len(latencies)}回)")
    else:
        print(f"{desc} 有効なレイテンシデータがありません。")
    return avg_latency


def main():
    print(f"Gemini PDFコンテキストキャッシュ レイテンシ比較 ({MODEL_NAME}, PDF: {PDF_GCS_URI})")
    print("--------------------------------------------------")

    gcp_project = os.getenv("GOOGLE_CLOUD_PROJECT")
    gcp_location = os.getenv("GOOGLE_CLOUD_LOCATION")
    use_vertexai = os.getenv("GOOGLE_GENAI_USE_VERTEXAI")

    print(f"Vertex AI 設定:")
    print(f"  GOOGLE_CLOUD_PROJECT: {gcp_project or '未設定 (ローカル実行の可能性)'}")
    print(f"  GOOGLE_CLOUD_LOCATION: {gcp_location or '未設定 (ローカル実行の可能性)'}")
    print(f"  GOOGLE_GENAI_USE_VERTEXAI: {use_vertexai or '未設定 (ローカル実行の可能性)'}")
    print("--------------------------------------------------")

    if use_vertexai == "True" and (not gcp_project or not gcp_location):
        print(
            "警告: GOOGLE_GENAI_USE_VERTEXAI=True ですが、GOOGLE_CLOUD_PROJECT または GOOGLE_CLOUD_LOCATION が設定されていません。"
        )
        print("Vertex AI を使用する場合、これらの環境変数を正しく設定してください。")
        print("--------------------------------------------------")

    cache_obj = None
    lat_no_cache, lat_with_cache = float('inf'), float('inf')

    try:
        pdf_part = types.Part.from_uri(file_uri=PDF_GCS_URI,
                                       mime_type="application/pdf")
        text_part_pdf_prompt = types.Part.from_text(text=PROMPT_PDF)

        caching_content_for_creation = types.Content(
            role="user", parts=[pdf_part, text_part_pdf_prompt])

        summary_content_for_query = types.Content(
            role="user",
            parts=[types.Part.from_text(text=PROMPT_SUMMARY)],
        )

        contents_for_no_cache = [
            caching_content_for_creation, summary_content_for_query
        ]

        lat_no_cache = measure_latency(contents_for_no_cache, "キャッシュなし")

        cache_name_display = f"pdf-cache-{uuid.uuid4()}"
        print(f"\nキャッシュ '{cache_name_display}' 作成中...")
        cache_creation_start = time.perf_counter()

        cache_cfg = types.CreateCachedContentConfig(
            http_options=http_options,
            ttl=CACHE_TTL,
            display_name=cache_name_display,
            contents=[caching_content_for_creation],
            system_instruction=SYS_INSTRUCTION,
        )
        cache_obj = client.caches.create(model=MODEL_NAME, config=cache_cfg)

        cache_creation_time = (time.perf_counter() - cache_creation_start)
        print(
            f"キャッシュ '{cache_obj.name}' 作成完了。所要時間: {cache_creation_time:.2f} 秒")

        lat_with_cache = measure_latency([summary_content_for_query],
                                         "キャッシュあり",
                                         cache_name=cache_obj.name)

    except Exception as e:
        print(f"処理中にエラーが発生しました: {e}")
        import traceback
        traceback.print_exc()
    finally:
        if cache_obj and hasattr(cache_obj, 'name'):
            try:
                client.caches.delete(name=cache_obj.name)
                print(f"\nキャッシュ '{cache_obj.name}' 削除完了。")
            except Exception as e_del:
                print(f"キャッシュ '{cache_obj.name}' の削除中にエラー: {e_del}")

    print("\n\n=== レイテンシ比較結果 ===")
    print(f"キャッシュなし: {lat_no_cache:.2f} ms | キャッシュあり: {lat_with_cache:.2f} ms")
    if lat_with_cache < lat_no_cache and lat_no_cache != float(
            'inf') and lat_with_cache != float('inf'):
        diff = lat_no_cache - lat_with_cache
        perc = (diff / lat_no_cache) * 100 if lat_no_cache > 0 else 0
        print(f"改善: 約 {diff:.2f} ms ({perc:.2f}%)")
    elif lat_no_cache == float('inf') or lat_with_cache == float('inf'):
        print("比較不可 (片方または両方の測定でエラーが発生しました)")
    else:
        if lat_with_cache > lat_no_cache:
            diff = lat_with_cache - lat_no_cache
            perc_increase = (diff / lat_no_cache
                             ) * 100 if lat_no_cache > 0 else float('inf')
            print(
                f"キャッシュによる改善は見られませんでした。逆に約 {diff:.2f} ms ({perc_increase:.2f}%) 遅くなりました。"
            )
        else:
            print("キャッシュによるレイテンシの変化はありませんでした。")
    print("--------------------------------------------------")


if __name__ == "__main__":
    main()
# $ python main.py
# Gemini PDFコンテキストキャッシュ レイテンシ比較 (gemini-2.0-flash-001, PDF: gs://cloud-samples-data/generative-ai/pdf/1706.03762v7.pdf)
# --------------------------------------------------
# Vertex AI 設定:
#   GOOGLE_CLOUD_PROJECT: xxx
#   GOOGLE_CLOUD_LOCATION: us-central1
#   GOOGLE_GENAI_USE_VERTEXAI: True
# --------------------------------------------------

# --- キャッシュなし レイテンシ測定 ---
# イテレーション 1/10: 4654.03 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 2/10: 3701.27 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 3/10: 2900.34 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 4/10: 3452.11 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 5/10: 4191.03 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 6/10: 4186.27 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 7/10: 3588.00 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 8/10: 3860.88 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 9/10: 3756.48 ms (最初のトークン) (キャッシュヒットなし)
# イテレーション 10/10: 3444.05 ms (最初のトークン) (キャッシュヒットなし)
# キャッシュなし 平均: 3773.45 ms (10回)

# キャッシュ 'pdf-cache-6fe977eb-4832-416a-a9b1-c8a59120e482' 作成中...
# キャッシュ 'projects/xxx/locations/us-central1/cachedContents/3260681446762741760' 作成完了。所要時間: 4.05 秒

# --- キャッシュあり レイテンシ測定 ---
# イテレーション 1/10: 2266.28 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 2/10: 2224.19 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 3/10: 2144.47 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 4/10: 2126.84 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 5/10: 2164.64 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 6/10: 2202.30 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 7/10: 2205.13 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 8/10: 2139.59 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 9/10: 2255.01 ms (最初のトークン) (キャッシュヒット確認)
# イテレーション 10/10: 1540.00 ms (最初のトークン) (キャッシュヒット確認)
# キャッシュあり 平均: 2126.84 ms (10回)

# キャッシュ 'projects/xxx/locations/us-central1/cachedContents/3260681446762741760' 削除完了。

# === レイテンシ比較結果 ===
# キャッシュなし: 3773.45 ms | キャッシュあり: 2126.84 ms
# 改善: 約 1646.60 ms (43.64%)
# --------------------------------------------------

検証結果と考察

上記の Python スクリプトを実行した結果、Context caching を使用しない場合の平均レイテンシが約 3773.45 ms であったのに対し、Context caching を使用した場合は約 2126.84 ms となり、約 1646.60 ms (43.64%) のレイテンシ削減が見られました。
注意:これらの数値は実行環境やタイミングによって変動する可能性があります。

まとめ

今回の検証で、Gemini の Context caching 機能は、特に PDF のような大きなコンテキストを繰り返し利用する場合に、API リクエストのレイテンシを大幅に削減し、コスト削減にも繋がる可能性があることが確認できました。

キャッシュ作成の初期コストはありますが、同じコンテキストを多用するアプリケーションでは、ユーザーエクスペリエンス向上とシステム効率化に貢献するでしょう。皆さんのアプリケーションでも、Context caching の活用を検討してみてはいかがでしょうか。

最後に

自社に専門人材がいない、リソースが足りない等の課題をお持ちの方に、エンジニア領域の支援サービス(Data Engineer Hub)をご提供しています。
お困りごとございましたら是非お気軽にご相談ください。

本件に関するお問い合わせは下記にて承ります。
株式会社DataCurrent
info@datacurrent.co.jp

人気のコラムランキング

PICK UP

企業のDX推進におけるダッシュボード内製化について

DXmarketingPICK UP コラムダッシュボード内製化

企業のDX推進に向けた人材教育支援について

GA4marketingPICK UP コラム内製化

【データプライバシーコラム】電気通信事業法改正の解説(2022年7月時点)

CMPPICK UP コラムデータプライバシーデータプライバシーコラム個人情報保護

CMP導入時の注意点

CMPPICK UP コラムデータプライバシーデータプライバシーコラム個人情報保護

TOPへ
戻る