🦾

FastAPIのJSONレスポンスに型を書くだけでレスポンスが速くなるらしい

に公開
2026/02/27
3件

2/25追記

ベンチマークの測り方が間違っていました。
response_modelも高速化の対象になるそうです。

間違った情報を掲載してしまい大変申し訳ありません🙇‍♂️
検証終わり次第ベンチマーク部分を更新します。

追記:ベンチマーク更新しました。明らかに倍以上のパフォーマンス出てます。

はじめに

2月23日にFastAPI 0.131.0がリリースされました。

https://twitter.com/FastAPI/status/2025627146866073881

中でも注目されたのが以下のツイートです。

https://twitter.com/FastAPI/status/2025613852394529114

要約すると、「戻り値に型だけ指定することで、レスポンスの速さが最大2倍になる」とのことです。

え?すごくないすか?

class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool

@app.get("/user")
def get_users():
    ...

これを...

class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool

@app.get("/user")
def get_users() -> list[User]:
    ...

または

class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool

@app.get("/user", response_model=list[User])
def get_users():
    ...

こうするだけでパフォーマンスが上がるらしいです。

なんで早くなったん?

以下のPRを読み解きます。

https://github.com/fastapi/fastapi/pull/14962

✨ Serialize JSON response with Pydantic (in Rust), when there's a Pydantic return type or response model

訳:Pydantic の戻り値型またはレスポンスモデルがある場合、Pydantic (Rust 内) を使用して JSON レスポンスをシリアル化します

めっちゃシンプルな説明ですね。
PydanticはPythonで使われているバリデーションライブラリで、主にAPI周りの入出力の型チェックに使われています。

PydanticでRust?と思われますが、実は現行のPydantic v2はRustで書かれており、v1に比べて倍以上の処理速度を実現しているとのこと。

早くなっただけでなく、v2ではデフォルトでjsonシリアライズに対応するようになり、Pydanticのみでシリアライズが可能になったそうです。
つまりRust側でjsonシリアライズすることで高速になったそうです。

もうちょい補足

大きく変わったのはPRのこの部分。

https://github.com/fastapi/fastapi/blob/880ad19087c65436da083118aad4b4b9f4da1503/fastapi/routing.py#L290-L291

dump_jsonが有効の場合、serialize_jsonを使ってjsonシリアライズをします。これがRustでシリアライズする関数になります。

https://github.com/fastapi/fastapi/blob/880ad19087c65436da083118aad4b4b9f4da1503/fastapi/_compat/v2.py#L202-L226

一方でdump_jsonが無効の場合は従来通りserializeが使われます。

https://github.com/fastapi/fastapi/blob/880ad19087c65436da083118aad4b4b9f4da1503/fastapi/_compat/v2.py#L177-L200

dump_pythonはpythonオブジェクトを返却するため、それをjsonに変換する手間がありました。
一応変換自体はRustでしているのですが、そこにPythonでのjsonシリアライズが加わるので若干遅くなっていたそうです。

Rustのパフォーマンスに加えて、pythonを経由せずにjsonシリアライズすることになり高速になったそうです。

ベンチマーク

見せてもらおうか、新しいPydanticの性能とやらを!
ということでベンチマークで速くなったのか検証します。

方法

以下の2つのエンドポイントを作成し、負荷テストで検証したいと思います。

  • 型ヒントなし
  • response_modelを使う方法
  • 戻り値の型ヒント(Return Type)を使う方法

大量のデータを返すシナリオが最も差が出やすいため、1,000個のアイテムを返す設定で比較します。(どんなエンドポイントや)

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    id: int
    name: str
    email: str
    is_active: bool

# ダミーデータ1,000件生成
test_data: list[User] = [
    User(
        id=i,
        name=f"User{i}",
        email=f"user{i}@example.com",
        is_active=i % 2 == 0
    )
    for i in range(1000)
]

@app.get("/untyped")
def get_users_old_way():
    """型ヒントなし"""
    return test_data

@app.get("/typed")
def get_users_new_way() -> list[User]:
    """戻り値型ヒントを使用"""
    return test_data

@app.get("/response-model", response_model=list[User])
def get_users_response_model():
    """response_modelを使用"""
    return test_data


@app.get("/health")
def health_check():
    """ヘルスチェック用"""
    return {"status": "ok"}

検証

こちらも2種で比較します。

  • 計測ツールheyを使って、それぞれに10000回リクエストを送る。
  • pythonで計測用スクリプトを書いて、各エンドポイントに対して 100回〜500回 程度のリクエストを送信する。

結果

10000回リクエスト

  • 型ヒントなし
$ hey -n 10000 -c 50 http://127.0.0.1:8000/untyped

Summary:
  Total:        62.2807 secs
  Slowest:      0.5658 secs
  Fastest:      0.0204 secs
  Average:      0.3101 secs
  Requests/sec: 160.5634
  
  Total data:   751710000 bytes
  Size/request: 75171 bytes

Response time histogram:
  0.020 [1]     |
  0.075 [10]    |
  0.130 [24]    |
  0.184 [135]   |■
  0.239 [867]   |■■■■■■■■
  0.293 [2685]  |■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.348 [4123]  |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.402 [1379]  |■■■■■■■■■■■■■
  0.457 [563]   |■■■■■
  0.511 [187]   |■■
  0.566 [26]    |


Latency distribution:
  10%% in 0.2372 secs
  25%% in 0.2774 secs
  50%% in 0.3056 secs
  75%% in 0.3401 secs
  90%% in 0.3874 secs
  95%% in 0.4230 secs
  99%% in 0.4797 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0000 secs, 0.0000 secs, 0.0027 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0000 secs
  req write:    0.0000 secs, 0.0000 secs, 0.0007 secs
  resp wait:    0.3100 secs, 0.0176 secs, 0.5658 secs
  resp read:    0.0000 secs, 0.0000 secs, 0.0097 secs

Status code distribution:
  [200] 10000 responses

平均0.3101秒、Requests/sec: 160.5634でした。
思ったより処理に時間かかっていそうです。

  • response_modelを使う方法
$ hey -n 10000 -c 50 http://127.0.0.1:8000/response-model

Summary:
  Total:        5.1662 secs
  Slowest:      0.0937 secs
  Fastest:      0.0057 secs
  Average:      0.0258 secs
  Requests/sec: 1935.6448
  
  Total data:   751710000 bytes
  Size/request: 75171 bytes

Response time histogram:
  0.006 [1]     |
  0.014 [4]     |
  0.023 [1748]  |■■■■■■■■■
  0.032 [7825]  |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.041 [122]   |■
  0.050 [94]    |
  0.058 [155]   |■
  0.067 [1]     |
  0.076 [19]    |
  0.085 [20]    |
  0.094 [11]    |


Latency distribution:
  10%% in 0.0227 secs
  25%% in 0.0237 secs
  50%% in 0.0247 secs
  75%% in 0.0258 secs
  90%% in 0.0275 secs
  95%% in 0.0300 secs
  99%% in 0.0541 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0000 secs, 0.0000 secs, 0.0022 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0000 secs
  req write:    0.0000 secs, 0.0000 secs, 0.0009 secs
  resp wait:    0.0257 secs, 0.0056 secs, 0.0914 secs
  resp read:    0.0000 secs, 0.0000 secs, 0.0163 secs

Status code distribution:
  [200] 10000 responses

平均レイテンシ0.0258秒、Requests/sec: 1935.6448でした。
型ヒントなしに比べて12倍以上のパフォーマンス差をつけてます。
普通にえぐいな...

  • 戻り値の型ヒント(Return Type)を使う方法
% hey -n 10000 -c 50 http://127.0.0.1:8000/typed

Summary:
  Total:        5.0625 secs
  Slowest:      0.0641 secs
  Fastest:      0.0049 secs
  Average:      0.0252 secs
  Requests/sec: 1975.3149
  
  Total data:   751710000 bytes
  Size/request: 75171 bytes

Response time histogram:
  0.005 [1]     |
  0.011 [3]     |
  0.017 [10]    |
  0.023 [990]   |■■■■■
  0.029 [8492]  |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  0.034 [237]   |■
  0.040 [20]    |
  0.046 [57]    |
  0.052 [70]    |
  0.058 [111]   |■
  0.064 [9]     |


Latency distribution:
  10%% in 0.0226 secs
  25%% in 0.0235 secs
  50%% in 0.0245 secs
  75%% in 0.0256 secs
  90%% in 0.0270 secs
  95%% in 0.0286 secs
  99%% in 0.0531 secs

Details (average, fastest, slowest):
  DNS+dialup:   0.0000 secs, 0.0000 secs, 0.0018 secs
  DNS-lookup:   0.0000 secs, 0.0000 secs, 0.0000 secs
  req write:    0.0000 secs, 0.0000 secs, 0.0009 secs
  resp wait:    0.0252 secs, 0.0048 secs, 0.0640 secs
  resp read:    0.0000 secs, 0.0000 secs, 0.0059 secs

Status code distribution:
  [200] 10000 responses

平均0.0252秒、1975.31 req/secでした。
こちらも同様に12倍以上の差をつけています。
response_modelと比べて0.0006秒短縮されました。一応パフォーマンス的にはどちらもあまり変わりませんが、体感こちらの方が早いケースが多かったです。

100回〜500回 程度のリクエスト

以下のような計測ツールで検証します。

ベンチマークスクリプト
import asyncio
import time
import statistics
import sys
import httpx
from typing import List

async def benchmark_endpoint(client: httpx.AsyncClient, url: str, requests_count: int) -> dict:
    """エンドポイントのベンチマークを実行"""
    response_times = []
    
    print(f"\n🔄 Testing {url} with {requests_count} requests...")
    
    # ウォームアップ(最初の数回は除外)
    for _ in range(5):
        await client.get(url)
    
    start_time = time.perf_counter()
    
    for i in range(requests_count):
        request_start = time.perf_counter()
        response = await client.get(url)
        request_end = time.perf_counter()
        
        if response.status_code != 200:
            print(f"❌ Error: Status code {response.status_code}")
            continue
            
        response_times.append((request_end - request_start) * 1000)  # ms
        
        if (i + 1) % 100 == 0:
            print(f"  Progress: {i + 1}/{requests_count} requests completed")
    
    total_time = time.perf_counter() - start_time
    
    return {
        "url": url,
        "total_requests": requests_count,
        "total_time": total_time,
        "avg_response_time": statistics.mean(response_times),
        "median_response_time": statistics.median(response_times),
        "min_response_time": min(response_times),
        "max_response_time": max(response_times),
        "rps": requests_count / total_time,
        "stdev": statistics.stdev(response_times) if len(response_times) > 1 else 0
    }

async def run_benchmarks(base_url: str, requests_count: int):
    """すべてのベンチマークを実行"""
    async with httpx.AsyncClient(base_url=base_url, timeout=30.0) as client:
        # ヘルスチェック
        print("🏥 Health check...")
        health_response = await client.get("/health")
        if health_response.status_code != 200:
            print("❌ Server is not healthy!")
            sys.exit(1)
        print("✅ Server is healthy\n")
        
        # ベンチマーク実行
        results = []
        
        # Untyped (型指定なし)
        untyped_result = await benchmark_endpoint(client, "/untyped", requests_count)
        results.append(("Untyped (no annotation)", untyped_result))
        
        # 少し待機
        await asyncio.sleep(1)
        
        # Typed (戻り値型ヒント)
        typed_result = await benchmark_endpoint(client, "/typed", requests_count)
        results.append(("Typed (return annotation)", typed_result))
        
        # 少し待機
        await asyncio.sleep(1)
        
        # Response model
        response_model_result = await benchmark_endpoint(client, "/response-model", requests_count)
        results.append(("Response Model", response_model_result))
        
        return results

def print_report(results: List[tuple], python_version: str, fastapi_version: str, pydantic_version: str):
    """結果レポートを出力"""
    print("\n" + "="*80)
    print("📊 BENCHMARK REPORT")
    print("="*80)
    
    print("\n【実行環境】")
    print(f"  Python:  {python_version}")
    print(f"  FastAPI: {fastapi_version}")
    print(f"  Pydantic: {pydantic_version}")
    
    print("\n【比較表】")
    print("-"*80)
    print(f"{'Method':<25} {'Avg (ms)':<12} {'Median (ms)':<12} {'RPS':<12} {'Stdev (ms)':<12}")
    print("-"*80)
    
    for name, result in results:
        print(f"{name:<25} {result['avg_response_time']:>10.2f}  "
              f"{result['median_response_time']:>10.2f}  "
              f"{result['rps']:>10.2f}  "
              f"{result['stdev']:>10.2f}")
    
    print("-"*80)
    
    # 比較分析
    untyped = results[0][1]
    typed = results[1][1]
    response_model = results[2][1]
    
    print("\n【考察】")
    print("-"*80)
    
    # 最速を特定
    fastest_idx = min(range(len(results)), key=lambda i: results[i][1]['avg_response_time'])
    fastest_name = results[fastest_idx][0]
    
    print(f"🏆 最速: {fastest_name}\n")
    
    # Untyped vs Typed
    untyped_vs_typed = ((untyped['avg_response_time'] - typed['avg_response_time']) 
                        / untyped['avg_response_time'] * 100)
    print(f"📊 Untyped vs Typed (return annotation):")
    if untyped_vs_typed > 0:
        print(f"   ✅ Typed の方が {untyped_vs_typed:.2f}% 高速")
    else:
        print(f"   ⚠️  Untyped の方が {abs(untyped_vs_typed):.2f}% 高速")
    print(f"   {untyped['avg_response_time']:.2f}ms → {typed['avg_response_time']:.2f}ms")
    print(f"   RPS: {untyped['rps']:.2f} → {typed['rps']:.2f} ({((typed['rps'] - untyped['rps']) / untyped['rps'] * 100):+.2f}%)")
    
    # Untyped vs Response Model
    untyped_vs_rm = ((untyped['avg_response_time'] - response_model['avg_response_time']) 
                     / untyped['avg_response_time'] * 100)
    print(f"\n📊 Untyped vs Response Model:")
    if untyped_vs_rm > 0:
        print(f"   ✅ Response Model の方が {untyped_vs_rm:.2f}% 高速")
    else:
        print(f"   ⚠️  Untyped の方が {abs(untyped_vs_rm):.2f}% 高速")
    print(f"   {untyped['avg_response_time']:.2f}ms → {response_model['avg_response_time']:.2f}ms")
    print(f"   RPS: {untyped['rps']:.2f} → {response_model['rps']:.2f} ({((response_model['rps'] - untyped['rps']) / untyped['rps'] * 100):+.2f}%)")
    
    # Typed vs Response Model
    typed_vs_rm = ((typed['avg_response_time'] - response_model['avg_response_time']) 
                   / typed['avg_response_time'] * 100)
    print(f"\n📊 Typed vs Response Model:")
    if typed_vs_rm > 0:
        print(f"   ✅ Response Model の方が {typed_vs_rm:.2f}% 高速")
    else:
        print(f"   ⚠️  Typed の方が {abs(typed_vs_rm):.2f}% 高速")
    print(f"   {typed['avg_response_time']:.2f}ms → {response_model['avg_response_time']:.2f}ms")
    print(f"   RPS: {typed['rps']:.2f} → {response_model['rps']:.2f} ({((response_model['rps'] - typed['rps']) / typed['rps'] * 100):+.2f}%)")
    
    print("\n💡 結論:")
    if abs(typed_vs_rm) < 5:
        print("   Typed (戻り値型ヒント) と Response Model のパフォーマンス差は微小(5%未満)。")
        print("   型ヒントの方がPythonらしい記述ができるため、可読性の観点から推奨されます。")
    elif typed_vs_rm < -5:
        print("   Typed (戻り値型ヒント) が Response Model より明確に高速です。")
    else:
        print("   Response Model が Typed より明確に高速です。")
    
    if abs(untyped_vs_typed) > 5 or abs(untyped_vs_rm) > 5:
        print("   型指定(Typed or Response Model)を行うことでパフォーマンスへの影響があります。")
    
    print("="*80)
    print()

async def main():
    """メイン処理"""
    import fastapi
    import pydantic
    
    # バージョン情報取得
    python_version = sys.version.split()[0]
    fastapi_version = fastapi.__version__
    pydantic_version = pydantic.__version__
    
    print("="*80)
    print("🚀 FastAPI Performance Benchmark")
    print("   Untyped vs Typed (return annotation) vs Response Model")
    print("="*80)
    
    base_url = "http://127.0.0.1:8000"
    requests_count = 500  # リクエスト数
    
    print(f"\n⚙️  Configuration:")
    print(f"   Server: {base_url}")
    print(f"   Requests per endpoint: {requests_count}")
    
    # ベンチマーク実行
    results = await run_benchmarks(base_url, requests_count)
    
    # レポート出力
    print_report(results, python_version, fastapi_version, pydantic_version)


if __name__ == "__main__":
    asyncio.run(main())

1回目
$ python benchmark.py                           
================================================================================
🚀 FastAPI Performance Benchmark
   Untyped vs Typed (return annotation) vs Response Model
================================================================================

⚙️  Configuration:
   Server: http://127.0.0.1:8000
   Requests per endpoint: 500
🏥 Health check...
✅ Server is healthy


🔄 Testing /untyped with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

🔄 Testing /typed with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

🔄 Testing /response-model with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

================================================================================
📊 BENCHMARK REPORT
================================================================================

【実行環境】
  Python:  3.13.0
  FastAPI: 0.131.0
  Pydantic: 2.12.5

【比較表】
--------------------------------------------------------------------------------
Method                    Avg (ms)     Median (ms)  RPS          Stdev (ms)  
--------------------------------------------------------------------------------
Untyped (no annotation)         7.14        7.04      140.06        0.57
Typed (return annotation)       1.35        1.33      740.09        0.18
Response Model                  1.34        1.32      745.92        0.14
--------------------------------------------------------------------------------

【考察】
--------------------------------------------------------------------------------
🏆 最速: Response Model

📊 Untyped vs Typed (return annotation):
   ✅ Typed の方が 81.08% 高速
   7.14ms → 1.35ms
   RPS: 140.06 → 740.09 (+428.39%)

📊 Untyped vs Response Model:
   ✅ Response Model の方が 81.23% 高速
   7.14ms → 1.34ms
   RPS: 140.06 → 745.92 (+432.56%)

📊 Typed vs Response Model:
   ✅ Response Model の方が 0.78% 高速
   1.35ms → 1.34ms
   RPS: 740.09 → 745.92 (+0.79%)

💡 結論:
   Typed (戻り値型ヒント) と Response Model のパフォーマンス差は微小(5%未満)。
   型ヒントの方がPythonらしい記述ができるため、可読性の観点から推奨されます。
   型指定(Typed or Response Model)を行うことでパフォーマンスへの影響があります。
================================================================================
2回目
$ python benchmark.py
================================================================================
🚀 FastAPI Performance Benchmark
   Untyped vs Typed (return annotation) vs Response Model
================================================================================

⚙️  Configuration:
   Server: http://127.0.0.1:8000
   Requests per endpoint: 500
🏥 Health check...
✅ Server is healthy


🔄 Testing /untyped with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

🔄 Testing /typed with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

🔄 Testing /response-model with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

================================================================================
📊 BENCHMARK REPORT
================================================================================

【実行環境】
  Python:  3.13.0
  FastAPI: 0.131.0
  Pydantic: 2.12.5

【比較表】
--------------------------------------------------------------------------------
Method                    Avg (ms)     Median (ms)  RPS          Stdev (ms)  
--------------------------------------------------------------------------------
Untyped (no annotation)         7.25        6.99      137.96        2.03
Typed (return annotation)       1.29        1.27      775.41        0.11
Response Model                  1.54        1.29      648.94        2.27
--------------------------------------------------------------------------------

【考察】
--------------------------------------------------------------------------------
🏆 最速: Typed (return annotation)

📊 Untyped vs Typed (return annotation):
   ✅ Typed の方が 82.21% 高速
   7.25ms → 1.29ms
   RPS: 137.96 → 775.41 (+462.06%)

📊 Untyped vs Response Model:
   ✅ Response Model の方が 78.75% 高速
   7.25ms → 1.54ms
   RPS: 137.96 → 648.94 (+370.39%)

📊 Typed vs Response Model:
   ⚠️  Typed の方が 19.49% 高速
   1.29ms → 1.54ms
   RPS: 775.41 → 648.94 (-16.31%)

💡 結論:
   Typed (戻り値型ヒント) が Response Model より明確に高速です。
   型指定(Typed or Response Model)を行うことでパフォーマンスへの影響があります。
================================================================================

3回目
$ python benchmark.py
================================================================================
🚀 FastAPI Performance Benchmark
   Untyped vs Typed (return annotation) vs Response Model
================================================================================

⚙️  Configuration:
   Server: http://127.0.0.1:8000
   Requests per endpoint: 500
🏥 Health check...
✅ Server is healthy


🔄 Testing /untyped with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

🔄 Testing /typed with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

🔄 Testing /response-model with 500 requests...
  Progress: 100/500 requests completed
  Progress: 200/500 requests completed
  Progress: 300/500 requests completed
  Progress: 400/500 requests completed
  Progress: 500/500 requests completed

================================================================================
📊 BENCHMARK REPORT
================================================================================

【実行環境】
  Python:  3.13.0
  FastAPI: 0.131.0
  Pydantic: 2.12.5

【比較表】
--------------------------------------------------------------------------------
Method                    Avg (ms)     Median (ms)  RPS          Stdev (ms)  
--------------------------------------------------------------------------------
Untyped (no annotation)         7.72        7.25      129.47        3.48
Typed (return annotation)       1.29        1.26      773.63        0.13
Response Model                  1.29        1.25      772.33        0.18
--------------------------------------------------------------------------------

【考察】
--------------------------------------------------------------------------------
🏆 最速: Typed (return annotation)

📊 Untyped vs Typed (return annotation):
   ✅ Typed の方が 83.27% 高速
   7.72ms → 1.29ms
   RPS: 129.47 → 773.63 (+497.54%)

📊 Untyped vs Response Model:
   ✅ Response Model の方が 83.24% 高速
   7.72ms → 1.29ms
   RPS: 129.47 → 772.33 (+496.53%)

📊 Typed vs Response Model:
   ⚠️  Typed の方が 0.17% 高速
   1.29ms → 1.29ms
   RPS: 773.63 → 772.33 (-0.17%)

💡 結論:
   Typed (戻り値型ヒント) と Response Model のパフォーマンス差は微小(5%未満)。
   型ヒントの方がPythonらしい記述ができるため、可読性の観点から推奨されます。
   型指定(Typed or Response Model)を行うことでパフォーマンスへの影響があります。
================================================================================

倍どころじゃないやん...
何度か試してみましたが4倍以上のパフォーマンスを出すケースが多かったです。

総評

2倍どころじゃないやん...
型ヒントがない場合に比べて明らかに速くなっています。
特に大量のリクエストを捌くときに真価を発揮してます。

終わりに

Pythonの型ヒントは保守性の面で付けられているた、性能自体が向上するわけではありません。2,3年ぐらい前Pythonサーバーサイドで厳密に型を定義して開発してたのですが、パフォーマンスに寄与しないと聞いてガッカリしたのを思い出しました。
しかしFastAPIではパフォーマンスにも影響を与えるようになりました。
そう言った事例が生まれる事は嬉しいし、どんどん広まって欲しいなと思いました。

ちなみに今FastAPIを使っている人は

  • 0.131.0にアップデート。
  • 戻り値に型ヒントを追加。

するだけで今回のようなパフォーマンスの恩恵を得られます。
ぜひアップデートしてみましょう!

GitHubで編集を提案

Discussion

td2sktd2sk

PRにreturn type or response modelとあるのでresponse modelでの指定も高速化の対象ではないでしょうか?
高速化の対象にならないのはresponse class指定の場合に見えます。

PR中で高速化されたと言及されているベンチマークでもresponse modelのみの指定でした
https://github.com/fastapi/fastapi/blob/880ad19087c65436da083118aad4b4b9f4da1503/tests%2Fbenchmarks%2Ftest_general_performance.py#L143-L145

2
マグロマグロ

コメントありがとうございます!
すいませんそうっぽいですね...手元で検証したところものすごい差が出たので修正しますね...

ご指摘ありがとうございます🙇‍♂️

2
マグロマグロ

だった今修正しました!
改めてご指摘ありがとうございます!

2