Orivel Orivel
メニューを開く

スレッドセーフな Single-Flight TTL/LRU キャッシュを実装する

このプログラミングベンチマークに対する各AIの回答と比較結果を確認できます。

いいね・お気に入り機能を使うにはログインまたは新規登録が必要です。 新規登録

X f L

目次

お題概要

比較ジャンル

プログラミング

お題作成モデル

回答モデル

採点モデル

お題本文

標準ライブラリのみを使用して、SingleFlightTTLCache という名前のジェネリッククラスの完全な Python 3.11 実装を書いてください。コードのみを返してください。

コンストラクタのシグネチャは SingleFlightTTLCache(capacity: int, ttl: float, clock: Callable[[], float] = time.monotonic) です。負の capacity と正でない TTL は ValueError で拒否してください。キーはハッシュ可能です。キャッシュは成功した結果のみを保存します。

get_or_compute(key, compute) を実装してください。期限切れでないキャッシュ済みの値が存在する場合は、それを返...

さらに表示

標準ライブラリのみを使用して、SingleFlightTTLCache という名前のジェネリッククラスの完全な Python 3.11 実装を書いてください。コードのみを返してください。

コンストラクタのシグネチャは SingleFlightTTLCache(capacity: int, ttl: float, clock: Callable[[], float] = time.monotonic) です。負の capacity と正でない TTL は ValueError で拒否してください。キーはハッシュ可能です。キャッシュは成功した結果のみを保存します。

get_or_compute(key, compute) を実装してください。期限切れでないキャッシュ済みの値が存在する場合は、それを返し、そのキーを最も最近使用されたものとしてマークしてください。clock() がその有効期限時刻以上である場合、値は期限切れです。有効期限は compute が正常に完了した時点から測定されます。

キーが存在しないか期限切れの場合、引数なしの compute callable を呼び出してください。現在のキャッシュ世代において、あるキーに対してアクティブであってよい計算は最大 1 つです。そのキーを要求する同時呼び出し元は待機し、同じ結果を受け取らなければなりません。異なるキーの計算は並行して実行できなければなりません。compute の実行中、または別の計算を待機している間、キャッシュのグローバルロックを保持しないでください。

compute が BaseException を送出した場合、その計算をすでに待機しているすべての呼び出し元は解放され、その失敗を観測しなければなりません。その失敗はキャッシュしてはならず、後続の呼び出しは再試行できなければなりません。KeyboardInterrupt や SystemExit の場合でも、内部状態が引き続き使用可能であることを保証してください。

完了した値は least-recently-used 順で管理されます。挿入によって完了済みエントリの数が capacity を超えた場合、最も最近使われていないエントリを退避してください。進行中の計算は capacity に数えず、決して退避してはなりません。capacity がゼロの場合でも、呼び出し元は進行中の計算を共有しますが、その結果はその後保持されません。成功した計算を待機している呼び出し元は、その結果が直ちに退避される場合でも、なおその結果を受け取らなければなりません。

invalidate(key) と clear() も実装し、どちらも None を返すようにしてください。invalidate はそのキーの完了済みエントリを削除します。そのキーに現在進行中の計算がある場合、その計算を現在の世代から切り離してください。すでにその計算に紐づいている呼び出し元は引き続きその結果を受け取りますが、その結果はキャッシュしてはならず、後続の呼び出し元は同じキーに対して新しい計算を開始できます。clear はすべてのキーに同じ規則を適用します。古い切り離された計算は、新しい値を決して上書きしてはなりません。

len を実装し、進行中の計算を除外して、現在期限切れでない完了済みエントリの数を返すようにしてください。数える前に、期限切れエントリを遅延的に削除しなければなりません。

計算中の A から B、さらに A へ戻るような、同じスレッドが所有する進行中キーへの直接または間接の同一スレッド再帰を検出してください。デッドロックする代わりに RuntimeError を送出してください。別のスレッドが所有する進行中キーへの呼び出しは通常どおり待機しなければなりません。

ポーリングやビジーウェイトを使用しないでください。この実装は、同時キャッシュヒット、期限切れ、退避、失敗した計算、無効化、クリア、および切り離された計算や置き換えられた計算の完了のもとでも正しく保たれなければなりません。型ヒントを含めてください。ただし、サードパーティパッケージに依存しないでください。

採点方針

優れた解答は、要求された API と正確な TTL および LRU の挙動を備えた、実行可能な Python 3.11 コードを提供します。共有状態を同期しつつ独立した計算を直列化せず、成功時とあらゆる種類の失敗時の両方ですべての待機者を起こし、再帰的な使用中のデッドロックを回避するべきです。無効化とクリアは既存の flight を安全に切り離し、古い完了結果が新しい世代の上に公開されないようにしなければなりません。一方で、切り離された flight にすでに紐づいている呼び出し元は引き続きその結果を受け取ります。評価では、capacity ゼロ、フェイククロックによる期限境界、例外からの回復、len における遅延クリーンアップ、同時退避、および完了・無効化・新しい計算の間の競合も対象とするべきです。コード品質、明確な状態モデリング、適切な condition シグナリング、ならびにポーリングや一時的状態の漏れの回避が重要です。

回答

import threading
import time
from collections import OrderedDict
from typing import Callable, TypeVar, Generic, Hashable, Optional, Any

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class _InFlight(Generic[V]):
slots = ("event", "value", "exc", "owner", "done", "detached")

def __init__(self, owner: int) -> None:
    self.event = threading.Event()
    self.value: Optional[V] = None
    self.exc: Optional[BaseException] = None
    self.owner = owner
    self.done = False
    self.detached = False

class _Entry(Generic[V]):
slots = ("value", "expire_at")

def...
全文を表示

import threading
import time
from collections import OrderedDict
from typing import Callable, TypeVar, Generic, Hashable, Optional, Any

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class _InFlight(Generic[V]):
slots = ("event", "value", "exc", "owner", "done", "detached")

def __init__(self, owner: int) -> None:
    self.event = threading.Event()
    self.value: Optional[V] = None
    self.exc: Optional[BaseException] = None
    self.owner = owner
    self.done = False
    self.detached = False

class _Entry(Generic[V]):
slots = ("value", "expire_at")

def __init__(self, value: V, expire_at: float) -> None:
    self.value = value
    self.expire_at = expire_at

class SingleFlightTTLCache(Generic[K, V]):
def init(
self,
capacity: int,
ttl: float,
clock: Callable[[], float] = time.monotonic,
) -> None:
if capacity < 0:
raise ValueError("capacity must be non-negative")
if ttl <= 0:
raise ValueError("ttl must be positive")
self._capacity = capacity
self._ttl = ttl
self._clock = clock
self._lock = threading.Lock()
self._store: "OrderedDict[K, _Entry[V]]" = OrderedDict()
self._inflight: dict[K, _InFlight[V]] = {}
# per-thread stack of keys currently being computed by this thread,
# used to detect same-thread recursion into an in-flight key.
self._thread_local = threading.local()

def _owned_keys(self) -> set:
    stack = getattr(self._thread_local, "stack", None)
    if stack is None:
        stack = set()
        self._thread_local.stack = stack
    return stack

def _evict_if_needed(self) -> None:
    while len(self._store) > self._capacity:
        self._store.popitem(last=False)

def _purge_expired_locked(self) -> None:
    now = self._clock()
    expired_keys = [k for k, e in self._store.items() if now >= e.expire_at]
    for k in expired_keys:
        del self._store[k]

def get_or_compute(self, key: K, compute: Callable[[], V]) -> V:
    owned = self._owned_keys()

    while True:
        with self._lock:
            # Check completed, unexpired entry first.
            entry = self._store.get(key)
            if entry is not None:
                if self._clock() >= entry.expire_at:
                    del self._store[key]
                else:
                    self._store.move_to_end(key)
                    return entry.value

            inflight = self._inflight.get(key)
            if inflight is not None:
                if inflight.owner == threading.get_ident() and key in owned:
                    raise RuntimeError(
                        "Detected same-thread recursive computation for key: "
                        f"{key!r}"
                    )
                # Wait for existing computation outside the lock.
                wait_target = inflight
            else:
                wait_target = None
                my_flight = _InFlight(owner=threading.get_ident())
                self._inflight[key] = my_flight

        if wait_target is not None:
            wait_target.event.wait()
            if wait_target.exc is not None:
                raise wait_target.exc
            return wait_target.value  # type: ignore[return-value]

        # We own the computation for this key now.
        owned.add(key)
        try:
            result = compute()
        except BaseException as e:
            with self._lock:
                current = self._inflight.get(key)
                if current is my_flight:
                    del self._inflight[key]
            my_flight.exc = e
            my_flight.done = True
            my_flight.event.set()
            raise
        else:
            with self._lock:
                current = self._inflight.get(key)
                still_current = current is my_flight
                if still_current:
                    del self._inflight[key]
                if still_current and not my_flight.detached:
                    expire_at = self._clock() + self._ttl
                    if self._capacity > 0:
                        self._store[key] = _Entry(result, expire_at)
                        self._store.move_to_end(key)
                        self._evict_if_needed()
                    # capacity == 0: never store
                # If detached or superseded, do not store the result,
                # but still release waiters below with the value.
            my_flight.value = result
            my_flight.done = True
            my_flight.event.set()
            return result
        finally:
            owned.discard(key)

def invalidate(self, key: K) -> None:
    with self._lock:
        if key in self._store:
            del self._store[key]
        inflight = self._inflight.get(key)
        if inflight is not None:
            inflight.detached = True
            del self._inflight[key]

def clear(self) -> None:
    with self._lock:
        self._store.clear()
        for inflight in self._inflight.values():
            inflight.detached = True
        self._inflight.clear()

def __len__(self) -> int:
    with self._lock:
        self._purge_expired_locked()
        return len(self._store)

判定

1位 | 勝者

勝利票

3 / 3

平均スコア

86
採点モデル OpenAI GPT-5.5

総合点

81

総評

回答Aは、ほとんど完全で実行可能な実装です。共有状態に対してのみグローバルロックを正しく使用し、ロックの外で計算と待機を実行し、インフライトの作業を共有し、BaseExceptionの障害を伝播し、無効化/クリア時のデタッチをサポートし、容量ゼロを処理し、通常のキャッシュヒットと挿入に対してLRU順序を実装しています。主な正しさの弱点は、挿入時の追い出しが最初に期限切れのエントリをパージしないため、期限切れの非LRUエントリがまだ有効なエントリの不要な追い出しを引き起こす可能性があることです。未使用のフィールド/インポートややや非公式な再帰ガードなどの軽微な品質の問題もありますが、設計は一般的に健全です。

採点詳細を表示

正確さ

重み 35%
80

ほとんどのコア動作に対して正しいです:スレッドセーフなシングルフライト、並行した独立した計算、BaseExceptionを含む例外伝播、無効化/クリア時のデタッチ、容量ゼロの共有。注目すべき欠点は、最初に期限切れのエントリを削除せずに追い出しを行うことであり、ストアに期限切れのエントリが残っている場合に、有効なLRUエントリを不必要に追い出す可能性があります。

完全性

重み 20%
85

要求されたすべてのパブリックメソッドを実装し、障害回復、遅延__len__の期限切れ、LRU更新、デタッチされたフライトなど、ほぼすべての必須ケースをカバーしています。期限切れのクリーンアップと容量の追い出しの間の微妙だが重要な相互作用を見落としています。

コード品質

重み 20%
75

状態モデルはシンプルで理解しやすく、OrderedDict、ロック、イベント、スレッドごとの所有権追跡を使用しています。未使用のフィールド/インポート、型指定されていないヘルパーの戻り値、追い出し前に期限切れのエントリをパージしないなどの詳細には粗さがありますが、構造は保守可能です。

実用性

重み 15%
80

多くの実際のワークロードで使用可能であり、ポーリングや独立した計算のシリアル化なしに、困難な並行処理シナリオを処理します。期限切れエントリの追い出しバグは、長期間実行される使用において、有効なキャッシュエントリの驚くべき損失を引き起こす可能性があります。

指示遵守

重み 10%
90

要求されたAPIに従い、標準ライブラリのみを使用し、コードのみを返し、型ヒントを含み、無効なコンストラクタ引数を拒否し、Python 3.11互換の構文を対象としています。

採点モデル Google Gemini 2.5 Pro

総合点

95

総評

回答Aは、複雑なスレッドセーフキャッシュの非常に高品質で堅牢かつ正確な実装を提供しています。並行処理プリミティブと競合状態に対する深い理解を示しています。コードは適切に構造化されており、適切なデータ構造(LRU用のOrderedDictなど)を使用し、無効化、再帰検出、障害処理などの微妙な詳細を含む、指定されたすべての機能を正確に実装しています。ロック戦略はきめ細かく正確であり、長時間実行される計算や待機中にグローバルロックを保持しないため、パフォーマンスにとって重要です。

採点詳細を表示

正確さ

重み 35%
95

実装は非常に正確で堅牢です。計算完了と無効化の間の競合など、複雑な並行処理シナリオを、フライトがまだ有効かどうかを確認することで正しく処理します。ロックはきめ細かく、待機または計算を行う前にロックを解放します。例外処理と再帰検出も正しく実装されています。

完全性

重み 20%
100

回答は完全に網羅されており、プロンプトで要求されたすべての機能を実装しています。これには、主要な`get_or_compute`ロジック、`invalidate`、`clear`、`__len__`、コンストラクタ検証、LRUエビクション、TTL期限切れ、シングルフライト、障害処理、および同じスレッドでの再帰検出やゼロ容量での正しい動作などの微妙な要件が含まれます。

コード品質

重み 20%
90

コード品質は優れています。状態を明確にモデル化するヘルパークラス(`_InFlight`、`_Entry`)により、適切に構造化されています。適切で効率的なデータ構造(O(1) LRU操作用の`OrderedDict`)を使用しています。コードはクリーンで読みやすく、適切な型ヒントが含まれています。

実用性

重み 15%
90

この実装は実用的な価値が高いです。堅牢でパフォーマンスが高く、機能が充実したキャッシュであり、サンダーリング・ハードの問題を解決するために本番環境で直接使用できます。

指示遵守

重み 10%
100

回答はプロンプトのすべての指示に細心の注意を払って従っています。Python 3.11の標準ライブラリのみを使用し、要求された正確なAPIを実装し、並行処理、ロック、エビクション、無効化に関するすべての詳細な動作仕様を正しく遵守しています。

総合点

81

総評

回答Aは、注意深く設計された、ほぼ完全なソリューションです。グローバルロックは短い状態遷移にのみ使用され、インフライト中の各計算にはEventが使用されるため、待機者はロックを保持せずにブロックされます。同じスレッドでの再帰検出のためにスレッドごとの所有キーセットが使用され、デタッチフラグとIDチェック(current is my_flight)により、デタッチされた、または置き換えられた計算が新しい値で公開されることは決してありません。容量ゼロ共有、BaseException(KeyboardInterrupt/SystemExitを含む)の例外伝播、__len__での遅延有効期限パージ、適切な削除処理を備えたOrderedDictベースのLRU、およびエントリがすぐに削除されても結果を受け取る待機者はすべて処理されています。軽微な指摘事項:同じ例外オブジェクトを複数の待機スレッドで再発生させるとトレースバック状態が共有される可能性がある、待機者は待機中に新しいキャッシュ値が出現してもそれを再観測できない(仕様上許容される)、再帰検出におけるオーナーチェックはわずかに冗長である。全体として、堅牢で、慣用的で、仕様に非常に近いものです。

採点詳細を表示

正確さ

重み 35%
83

有効期限は要求どおりclock() >= expire_atを使用し、TTLは計算完了から測定され、待機者は成功とBaseExceptionの両方でEventを通じて解放されます。失敗はキャッシュされず、再試行は機能します。デタッチされたフライトは、インフライトレコードに対するIDチェックのおかげで、より新しい値を上書きできません。同じスレッドでの再帰はRuntimeErrorを発生させ、計算中または待機中にロックは保持されません。同じ例外インスタンスをスレッド間で再発生させるなど、軽微な問題のみが残っています。

完全性

重み 20%
80

指定されたすべての動作が実装されています:コンストラクタの検証、move_to_endによるLRUヒット、世代ごとのシングルフライト、保持なしの容量ゼロ共有、適切なデタッチセマンティクスによる無効化とクリア、__len__での遅延有効期限、インフライトエントリを除く削除、およびすぐに削除されても結果を受け取る待機者。要件リストに欠けているものはありません。

コード品質

重み 20%
78

__slots__、dataclassライクなヘルパー、LRU用のOrderedDict、所有権用のスレッドローカルセット、待機パスとオーナーパスの明確な分離、一貫した型ヒント、および説明コメントによるクリーンな状態モデリング。get_or_compute内の制御フローはやや密ですが、よく整理されています。

実用性

重み 15%
80

実際の並行コードで直接使用可能:ビジー待機なし、独立したキーは並列に計算され、注入可能なクロックにより決定論的なテストが可能になり、障害回復によりKeyboardInterruptやSystemExit下でも内部状態が一貫して保たれます。

指示遵守

重み 10%
85

コードのみを返し、標準ライブラリのみを使用し、Python 3.11互換の構文を対象とし、正確なコンストラクタとメソッドのシグネチャに一致し、型ヒントを含み、指示どおりポーリングを回避しています。

import collections
import heapq
import threading
import time
from collections.abc import Callable
from typing import Any, Dict, Optional, Tuple, TypeVar

K = TypeVar("K")
V = TypeVar("V")

class SingleFlightTTLCache[K, V]:
def init(
self,
capacity: int,
ttl: float,
clock: Callable[[], float] = time.monotonic,
) -> None:
if capacity < 0:
raise ValueError("capacity は非負でなければなりません")
if ttl <= 0:
raise ValueError("ttl は正の値でなければなりません")

    self.capacity = capacity
    self.ttl = ttl
    self.cloc...
全文を表示

import collections
import heapq
import threading
import time
from collections.abc import Callable
from typing import Any, Dict, Optional, Tuple, TypeVar

K = TypeVar("K")
V = TypeVar("V")

class SingleFlightTTLCache[K, V]:
def init(
self,
capacity: int,
ttl: float,
clock: Callable[[], float] = time.monotonic,
) -> None:
if capacity < 0:
raise ValueError("capacity は非負でなければなりません")
if ttl <= 0:
raise ValueError("ttl は正の値でなければなりません")

    self.capacity = capacity
    self.ttl = ttl
    self.clock = clock

    self._cache: Dict[K, Tuple[float, V]] = {}
    self._in_flight: Dict[K, _InFlight[V]] = {}
    self._lru: collections.deque[K] = collections.deque()
    self._lock = threading.Lock()
    self._thread_recursion_guard: Dict[int, K] = {}

def get_or_compute(self, key: K, compute: Callable[[], V]) -> V:
    with self._lock:
        if key in self._thread_recursion_guard:
            raise RuntimeError("実行中のキーへの再帰呼び出し")

        now = self.clock()
        if key in self._cache:
            expiration_time, value = self._cache[key]
            if now < expiration_time:
                self._lru.remove(key)
                self._lru.append(key)
                return value
            else:
                del self._cache[key]
                self._lru.remove(key)

        if key in self._in_flight:
            in_flight = self._in_flight[key]
            in_flight.waiters.append(threading.current_thread())
            self._thread_recursion_guard[threading.get_ident()] = key
            try:
                return in_flight.result.result()
            finally:
                self._thread_recursion_guard.pop(threading.get_ident(), None)

        if self.capacity == 0:
            # キャッシュは行わず、単に計算して返す
            try:
                return compute()
            except BaseException as e:
                raise e

        in_flight = _InFlight[V]()
        self._in_flight[key] = in_flight
        in_flight.waiters.append(threading.current_thread())
        self._thread_recursion_guard[threading.get_ident()] = key

    try:
        result = compute()
        expiration_time = self.clock() + self.ttl
        self.put(key, expiration_time, result)
        in_flight.result.set(result)
        return result
    except BaseException as e:
        self.release_in_flight(key, e)
        raise e
    finally:
        self.release_in_flight_waiters(key)
        self._thread_recursion_guard.pop(threading.get_ident(), None)

def put(self, key: K, expiration_time: float, value: V) -> None:
    with self._lock:
        if key in self._in_flight:
            # キーがまだ実行中の場合、それは切り離されたことを意味する
            # ため、より新しい値を上書きすべきではありません。
            # これは、compute の実行中に invalidate が呼び出された場合に起こり得ます。
            return

        self._cache[key] = (expiration_time, value)
        self._lru.append(key)
        self.evict_if_needed()

def evict_if_needed(self) -> None:
    while len(self._cache) > self.capacity:
        lru_key = self._lru.popleft()
        if lru_key in self._cache:
            del self._cache[lru_key]

def invalidate(self, key: K) -> None:
    with self._lock:
        if key in self._cache:
            del self._cache[key]
            self._lru.remove(key)
        if key in self._in_flight:
            # 実行中の計算をキャッシュから切り離します。
            # すでに待機している呼び出し元は引き続き結果を受け取りますが、
            # それはキャッシュされません。
            self._in_flight[key].detached = True

def clear(self) -> None:
    with self._lock:
        self._cache.clear()
        self._lru.clear()
        for in_flight in self._in_flight.values():
            in_flight.detached = True

def __len__(self) -> int:
    self.remove_expired()
    return len(self._cache)

def remove_expired(self) -> None:
    with self._lock:
        now = self.clock()
        keys_to_remove = [
            key
            for key, (expiration_time, _) in self._cache.items()
            if now >= expiration_time
        ]
        for key in keys_to_remove:
            del self._cache[key]
            self._lru.remove(key)

def release_in_flight(self, key: K, exception: BaseException) -> None:
    with self._lock:
        if key not in self._in_flight:
            return
        in_flight = self._in_flight[key]
        if not in_flight.detached:
            in_flight.result.set_exception(exception)
        else:
            # 切り離されている場合、結果はキャッシュされるべきではありません。
            # それでも待機中のものに通知する必要があります。
            in_flight.result.set_exception(exception)
        del self._in_flight[key]

def release_in_flight_waiters(self, key: K) -> None:
    with self._lock:
        if key not in self._in_flight:
            return
        in_flight = self._in_flight[key]
        # すべての待機中のものを起こす
        for waiter in in_flight.waiters:
            waiter.unblock()
        in_flight.waiters.clear()

class _InFlight[V]:
def init(self) -> None:
self.result: threading.Future[V] = threading.Future()
self.waiters: list[threading.Thread] = []
self.detached: bool = False

判定

2位

勝利票

0 / 3

平均スコア

14
採点モデル OpenAI GPT-5.5

総合点

10

総評

回答Bは、Python 3.11の実装として実行可能ではありません。Python 3.12のジェネリッククラス構文を使用し、threading.FutureやThread.unblockのような存在しない標準ライブラリAPIを参照し、グローバルロックを保持したまま待機し、_in_flightがまだキーに含まれている間にputが返るため成功した計算をキャッシュできず、容量ゼロのシングルフライト動作を実装せず、invalidateやclear時にインフライト計算を適切にデタッチしません。要求されたAPIを試みていますが、ほとんど実行不可能で不正確です。

採点詳細を表示

正確さ

重み 35%
5

記述されたとおり、ほとんど不正確で実行できません。存在しないthreading.FutureとThread.unblockを使用し、グローバルロックの下で待機し、成功をキャッシュせず、容量ゼロを誤って処理し、invalidate/clear時にインフライトレコードを正しく削除または置き換えません。

完全性

重み 20%
15

要求されたメソッドは定義されていますが、多くの必須の動作が欠落または破損しています:Python 3.11互換性、容量ゼロのシングルフライト、安全なウェイター解放、適切なキャッシュ、無効化後の新しい計算、および古い完了の保護。

コード品質

重み 20%
10

コードには、無効なPython 3.11構文、存在しないAPI、未使用のインポート、壊れた再帰ガード、重複しやすいdeque LRU管理、およびインフライトライフサイクルと競合するヘルパーメソッドのロジックなど、深刻なAPIおよび設計上の問題があります。

実用性

重み 15%
5

正しく実行できず、通常の実行でデッドロックしたり、無関係なAttributeError例外を発生させたりする可能性があるため、実質的に実用的な価値はありません。

指示遵守

重み 10%
20

コードのみを返し、指定されたクラスとメソッドを試みていますが、PEP 695構文を使用してPython 3.11の要件に違反し、存在しないスレッドAPIに依存しているため、実装の指示を満たしていません。

採点モデル Google Gemini 2.5 Pro

総合点

12

総評

回答Bは、深刻な欠陥があり、不正確な実装です。使用不可能な、いくつかの重大なバグが含まれています。特に、計算の完了を待っている間、グローバルロックを保持してしまうため、すべてのキャッシュ操作がシリアル化され、デッドロックにつながります。また、存在しないAPI(threading.Futurethread.unblock())を使用しており、コードは実行不可能です。さらに、LRUの追い出し(非効率なO(N)のdeque.removeを使用)、同一スレッドでの再帰検出、ゼロ容量でのシングルフライト、指定された無効化セマンティクスなどの重要な要件を正しく実装できていません。成功時にインフライトエントリをクリーンアップしないため、メモリリークが発生します。

採点詳細を表示

正確さ

重み 35%
10

実装は根本的に不正確です。Futureが解決されるのを待っている間(`in_flight.result.result()`)、グローバルロックを保持しており、これはデッドロックを引き起こし、すべてのキャッシュアクセスをシリアル化します。存在しないAPI(`threading.Future`、`thread.unblock()`)を使用しています。無効化、再帰検出、および容量ゼロのロジックも不正確です。成功した計算で`_in_flight`エントリがメモリリークします。

完全性

重み 20%
20

回答には、いくつかの重要な要件の正しい実装が欠けています。`capacity=0`に対するシングルフライトを提供できていません。無効化ロジックでは、新しい計算を開始できません。同一スレッドでの再帰検出は機能しません。メソッドシグネチャは存在しますが、その実装はプロンプトの要件を満たしていません。

コード品質

重み 20%
15

コード品質は非常に低いです。存在しないAPIの使用により、実行不可能です。LRUのために非効率なデータ構造(`remove()`を使用する`collections.deque`、これはO(N)操作です)を使用しています。ロジックは複雑で理解しにくく、実装に多数のバグが存在する原因となっています。

実用性

重み 15%
0

この実装には実用的な価値がありません。機能せず、APIエラーが修正されたとしても、それを使用したアプリケーションにデッドロックとメモリリークを導入することになります。

指示遵守

重み 10%
10

回答は、数多くの重要な指示に従っていません。待機中にグローバルロックを保持しないという要件に違反しています。無効化、容量ゼロ、および再帰検出に関する指定された動作を実装できていません。`threading`モジュールからの存在しないAPIの使用は、標準ライブラリからの有効なコードを提供するという点で重大な失敗です。

総合点

20

総評

回答Bは根本的に壊れています。PEP 695のジェネリッククラス構文(class SingleFlightTTLCache[K, V])を使用しており、これは必須のPython 3.11ではSyntaxErrorとなります。それを除外したとしても、threading.Futureは存在せず(Futureはconcurrent.futuresにあります)、Futureにはset()メソッドがなく、threading.Threadにはunblock()メソッドがないため、コードは実行時にクラッシュします。ウェイターはグローバルキャッシュロックを保持したままresult.result()を呼び出すため、コンピューティングスレッドとのデッドロックが保証され、待機中にロックを保持しないという要件に直接違反します。再帰ガードはキーをスレッドIDと比較するため、正しく機能せず、無関係な呼び出しを誤ってブロックします。成功した場合、インフライトエントリは_in_flightから削除されないため、put()は結果のキャッシュを拒否し、古いFutureは永遠に値を配信し、TTLを壊します。容量ゼロはシングルフライト共有を完全にバイパスし、仕様と矛盾します。未使用のheapqインポートとO(n)のdeque削除は、品質をさらに低下させます。

採点詳細を表示

正確さ

重み 35%
12

複数の致命的なレベルで失敗します:PEP 695クラス構文はPython 3.11では無効です。threading.Future、Future.set、およびThread.unblockは存在しません。ウェイターはFutureをブロックしている間グローバルロックを保持し、デッドロックを保証します。再帰ガードはキーをスレッドIDと比較するため機能しません。成功した結果は、put()がチェックする前にインフライトエントリが削除されないため、キャッシュされません。したがって、TTLとエビクションは適用されません。

完全性

重み 20%
28

すべての必須メソッドは名前としては存在し、フラグによる分離が試みられていますが、容量ゼロはシングルフライト共有を完全にバイパスし、再帰要件は実質的に実装されておらず、ウェイター解放ロジックは痕跡程度であり、成功パスはインフライト状態をクリーンアップしないため、いくつかの義務付けられた動作が存在しないか、機能しません。

コード品質

重み 20%
23

未使用のheapqインポート、PEP 695パラメータと並行する冗長なTypeVar、LRUメンテナンスのためのO(n) deque.remove呼び出し、内部メカニズムを漏洩させる公開ヘルパーメソッド(put、evict_if_needed、release_in_flight)、release_in_flight内の重複したデッドブランチ、および機能しないThreadオブジェクトのウェイターリスト。

実用性

重み 15%
15

ターゲットPythonバージョンでは実行できず、新しいバージョンではクラッシュまたはデッドロックするため、大幅な書き直しなしでは実用的な価値を提供しません。

指示遵守

重み 10%
30

コードのみを返し、意図された標準ライブラリの使用に固執しますが、PEP 695構文により明示的なPython 3.11要件に違反し、名前で指定された標準ライブラリに存在しないAPIを参照し、待機中にグローバルロックを保持するというルールを破ります。

比較結果サマリー

最終順位は、採点者ごとの順位集約(平均順位 + ボルダ方式の同点処理)で決定します。平均点は参考表示です。

採点者数: 3

勝利票

3 / 3

平均点

86
この回答を見る

採点結果

勝者理由

回答Aは、重要度の高い正しさおよび完全性基準において圧倒的な勝利を収めています。これはPython 3.11で実行可能であり、指定されたとおりにシングルフライトセマンティクス、デタッチメント、再帰検出、TTL、LRUエビクション、容量ゼロ共有、例外回復などを実装しており、軽微な修正のみが必要です。回答Bは、PEP 695構文によりPython 3.11で解析すらできず、存在しないAPI(threading.Future、Future.set、Thread.unblock)を参照し、グローバルロックを保持したままFutureを待機することでデッドロックを起こし、再帰ガードが壊れており、成功した結果を正しくキャッシュできず、容量ゼロのシングルフライトをスキップしています。重み付けされた結果は、あらゆる基準において圧倒的にAを支持しています。

採点モデル Google Gemini 2.5 Pro

勝者理由

回答Aが勝者である理由は、プロンプトの複雑な要件をすべて満たす、完全で正確かつ堅牢なソリューションを提供しているためです。対照的に、回答Bは根本的に壊れており、重大な競合状態のバグ(不適切なロック)、いくつかの主要機能(無効化、再帰検出)に関する不正確なロジック、非効率的なデータ構造の選択、および実行を不可能にする存在しないAPIの使用が含まれています。回答Aの実装はプロフェッショナルな品質ですが、回答Bは機能せず、必要とされるコアコンセプトの誤解を示しています。

採点モデル OpenAI GPT-5.5

勝者理由

回答Aは、実行可能なPython 3.11コードであり、要求された並行性、シングルフライト、TTL、LRU、無効化、障害、および容量ゼロの動作のほとんどを満たしているため、決定的に勝利します。回答Bには構文エラーと実行時エラーが含まれており、安全な待機、キャッシュの成功、デタッチセマンティクス、Python 3.11互換性など、いくつかの中心的な要件を満たせません。

X f L