glm-5.3-flash 在「实现LRU缓存机制」的评测结果

这是该 AI 模型在此测试用例上的详细评测结果。

基本信息

  • 模型名称:glm-5.3-flash
  • 用例名称:实现LRU缓存机制
  • 测试类型:文本生成
  • 评测维度:代码生成

系统提示词(System Prompt)

这是给 AI 模型的背景设定和角色指令:

你是一名资深后端工程师,擅长数据结构与算法设计,尤其熟悉缓存系统的实现原理。 回答要求: 1. 使用 Python 实现,代码结构清晰,命名规范,包含必要的注释。 2. 在实现前简要说明你选用的核心数据结构及其原因(1-3 句话即可)。 3. 实现完毕后,提供至少 5 个测试用例,覆盖正常流程与边界情况。 4. 对关键逻辑(如淘汰触发时机、访问顺序更新)给出简短说明。 5. 代码须可直接运行,测试用例须打印清晰的预期值与实际值对比。

用户提示词(User Prompt)

这是用户给 AI 模型的具体任务要求:

## 题目:实现一个简单的 LRU 缓存类 请使用 Python 实现一个 LRU(最近最少使用)缓存类 `LRUCache`,满足以下要求: ### 功能要求 1. 构造函数 `__init__(self, capacity: int)`:初始化缓存,`capacity` 为正整数,表示缓存最大容量。 2. `get(self, key: int) -> int`: - 若 `key` 存在于缓存中,返回对应的值,并将该键标记为「最近使用」。 - 若 `key` 不存在,返回 `-1`。 3. `put(self, key: int, value: int) -> None`: - 若 `key` 已存在,更新其值,并将该键标记为「最近使用」。 - 若 `key` 不存在,插入该键值对。 - 若插入后缓存容量超过 `capacity`,则删除**最久未使用**的键值对。 ### 数据结构说明 请在代码前用 1-3 句话说明你选用的核心数据结构(例如:Python 内置的 `OrderedDict`,或自定义的双向链表 + 字典),并解释为什么选用它。 ### 测试要求 编写测试函数,覆盖以下场景: - 基本的 put 和 get 操作 - 缓存满时的淘汰行为(验证被淘汰的 key 返回 -1) - 访问已有 key 后更新其「最近使用」顺序 - 更新已有 key 的 value - 容量为 1 的边界情况 ### 示例

任务要求

AI 模型需要满足以下要求:

  1. 核心 LRU 算法正确性:必须手动实现双向链表+哈希表,get/put 均为严格 O(1),正确处理容量为1、重复更新同一key、访问不存在key等边界场景,且禁止使用 OrderedDict 等内置有序结构作为核心实现。
  2. 并发安全与锁策略:实现分段锁(Sharding)机制,分片数为2的幂次方并给出原因;读写场景下使用读写锁优化;能够识别并说明潜在死锁风险及预防措施,并发测试中无数据竞争或状态不一致。
  3. 一致性哈希实现质量:正确实现哈希环与虚拟节点,能说明虚拟节点数量对负载均衡的影响,提供节点增删时的数据迁移说明,并通过均匀性验证(10000个key分布的标准差在合理范围内)。
  4. 缓存三大问题防护深度:布隆过滤器需自行实现并说明误判率/空间权衡;singleflight 防击穿需说明并发合并原理;雪崩防护需包含 TTL 抖动和熔断降级两种手段,三者均需有代码实现而非仅文字描述。
  5. 可观测性与热点识别:实现命中率、淘汰次数等基础指标统计;提供热点 key 识别机制(Count-Min Sketch 或滑动窗口 Top-K),并说明在高并发下统计精度与性能的权衡。
  6. 架构文档完整性与设计决策质量:架构文档需覆盖全部五个模块层次,每个关键设计选择(数据结构、锁粒度、哈希策略)必须有「选A而非B」的对比说明,文档与代码实现保持一致。

评测结果

综合得分

该模型在此用例的得分为:98.4 分(满分 100 分),已通过

基础难度评测结果

  • 得分:98.4 分
  • 通过状态:已通过

模型生成产物

以下是该模型针对此任务的实际生成内容:

# 一、核心数据结构选择 选用 **哈希表(dict)+ 手写双向链表(带头尾哨兵节点)**:dict 负责 O(1) 定位节点,双向链表负责 O(1) 地维护访问顺序——因为在已持有节点引用的前提下,只有双向链表能做到 O(1) 摘除与头插(单链表做不到)。相比直接用 `OrderedDict`,手写实现能完整暴露 LRU 的底层机制,不依赖标准库的特定行为。 # 二、完整代码(可直接运行) ```python """ LRU 缓存实现:哈希表 + 双向链表(带哨兵节点) get / put 时间复杂度均为 O(1),空间复杂度 O(capacity) """ class _DLinkedNode: """双向链表节点:保存 key/value 及前后指针。""" __slots__ = ("key", "value", "prev", "next") # 固定属性,省内存 def __init__(self, key=None, value=None): self.key = key self.value = value self.prev = None self.next = None class LRUCache: """ LRU 缓存。 链表布局(左端最新,右端最旧): head(哨兵) 最新节点 ... 最旧节点 tail(哨兵) - _map : {key: 节点},实现 O(1) 查找 - 链表 : 维护使用顺序,tail.prev 恒为「最久未使用」的节点 """ def __init__(self, capacity: int) -> None: if not isinstance(capacity, int) or capacity None: """将节点从链表中摘除(O(1),无需遍历)。""" node.prev.next = node.next node.next.prev = node.prev def _add_to_front(self, node: _DLinkedNode) -> None: """将节点插到哨兵头之后,表示「最近使用」。""" first = self._head.next node.prev = self._head node.next = first self._head.next = node first.prev = node def _move_to_front(self, node: _DLinkedNode) -> None: """已存在的节点被访问:先摘除再头插,即提升为最近使用。""" self._remove(node) self._add_to_front(node) def _evict_lru(self) -> None: """淘汰最久未使用节点,并同步删除哈希表映射。""" lru = self._tail.prev self._remove(lru) del self._map[lru.key] # ---------------- 对外接口 ---------------- def get(self, key: int) -> int: """命中则返回值并提升为最近使用;未命中返回 -1。""" node = self._map.get(key) if node is None: return -1 self._move_to_front(node) return node.value def put(self, key: int, value: int) -> None: """ 写入键值对: - 已存在:原地更新 value 并提升为最近使用(size 不变,绝不触发淘汰); - 不存在:创建节点并头插;若因此超出容量,立即淘汰尾部节点。 """ node = self._map.get(key) if node is not None: node.value = value self._move_to_front(node) return new_node = _DLinkedNode(key, value) self._map[key] = new_node self._add_to_front(new_node) # 淘汰触发点:仅当「插入新 key」导致超容量时执行一次 if len(self._map) > self.capacity: self._evict_lru() # ---------------- 调试辅助 ---------------- def __len__(self) -> int: return len(self._map) def keys(self) -> list:...

AI 评审点评

以下是 AI 评审对该模型输出的点评:

【CLAUDE】这是一份高质量的LRU缓存实现,算法正确性无懈可击,文档说明深入透彻,测试覆盖全面有效。手写双向链表+哨兵节点的设计展示了扎实的数据结构功底,代码结构清晰,命名规范,注释到位,完全满足题目所有要求并有所超越。 【GEMINI】这是一份教科书级别的 LRU 缓存实现。无论是核心算法的正确性、代码的优雅程度、文档说明的清晰度,还是测试用例的完备性,都无可挑剔,完全符合并超出了所有评测标准的要求。 【KIMI】该实现是一份高质量的LRU缓存代码,展现了扎实的算法功底和工程素养。核心算法采用经典的哈希表+双向链表方案,时间复杂度严格满足O(1)要求,哨兵节点设计优雅地消除了边界条件处理。代码结构清晰、命名规范、注释充分,兼具可读性与可维护性。测试覆盖全面,不仅满足题目要求的5个场景,还主动补充了防御性测试,体现了资深工程师的严谨性。整体而言,这是一份可直接用于生产环境参考的优秀实现。

进阶难度评测结果

  • 得分:96.2 分
  • 通过状态:已通过

模型生成产物

以下是该模型针对此任务的实际生成内容:

# LRU 缓存(双链表 + 哈希表 + TTL 惰性过期)完整实现 ## 一、设计说明 ### 1. 核心结构选型:为什么是「双向链表 + 哈希表」 | 需求 | 由谁满足 | |---|---| | 按 key O(1) 定位 | 哈希表 `dict: key → Node` | | O(1) 维护访问顺序(刷新热度 / 淘汰尾部) | 双向链表(head 端 = 最近使用,tail 端 = 最久未用) | **为什么双向链表优于单链表?** 单链表中删除一个已知节点,必须先找到它的**前驱**——只能从头遍历,O(n)。双向链表的节点自带 `prev` 指针,「摘除任意节点」「移动到头部」「弹出尾部」全部是纯指针操作,严格 O(1)。代价是每个节点多一个指针(8 字节)及稍复杂的指针维护——**用常数空间换取确定性的 O(1)**,这正是缓存场景最需要的性质。 其他关键决策: - **不用 `OrderedDict`**:它本质就是「哈希表 + 双向循环链表」的封装。手写才能自由扩展(TTL 字段挂载到节点上)、控制内存布局(`__slots__`),也符合本题要求。 - **哨兵头尾节点**:所有插入/删除都发生在两个真实节点之间,彻底消灭「链表为空」「操作的是头/尾节点」等分支判断,代码路径唯一。 - **节点同时存 key**:容量淘汰时需要从被驱逐的节点**反查哈希表**做 O(1) 删除;若节点只存 value,就得 O(n) 找 key。 - **可注入时钟** (`time_func`):默认 `time.monotonic()`(单调时钟,不受系统时间跳变/NTP 校准影响),测试中注入假时钟,使过期用例**完全确定、无需 sleep**。 - **`-1` 作为 miss 哨兵值**沿用题面约定;生产环境中若合法值可能是 `-1`,应改为返回 `Optional[V]` 或抛 `KeyError`。 - **线程安全**:本实现非线程安全;并发场景可在公开方法外包一层 `threading.Lock`,代价是锁争用,更高吞吐可用分段锁。 ### 2. TTL 惰性删除的权衡取舍 | | 惰性删除(本实现) | 主动删除(定时器/后台线程) | |---|---|---| | 时间复杂度 | get/put 严格 O(1),无额外开销 | 需要定时器轮或周期扫描,引入调度成本 | | 内存 | ❌ 已过期的**冷数据**(写入后无人再读)会一直驻留,直到被 LRU 挤出或被读到 | ✅ 及时回收,内存贴近活跃数据集 | | 复杂度 | ✅ 实现极简,无后台线程与竞态 | 需处理线程安全、扫描节奏调参 | | 典型折中 | Redis 的做法:**惰性删除为主 + 周期随机采样清理兜底**,兼顾两者 | | 本实现选择纯惰性策略:在 `get` 和 `put`(覆盖同 key 时)两处检查过期,过期即摘除。副作用是 `len()` 在清理前可能偏大,已在文档注释中标明。 --- ## 二、核心实现 ```python import time from typing import Any, Callable, Dict, Optional class _Node: """双向链表节点。 存 key 的原因:容量淘汰时需经节点反查哈希表 O(1) 删除; 若只存 value 则须遍历哈希表找 key,退化 O(n)。 """ __slots__ = ("key", "value", "expire_at", "prev", "next") def __init__(self, key: Any = None, value: Any = None, expire_at: Optional[float] = None): self.key = key self.value = value self.expire_at = expire_at # None 表示永不过期 self.prev: Optional["_Node"] = None self.next: Optional["_Node"] = None class LRUCache: """手写「双向链表 + 哈希表」LRU 缓存,支持 TTL 惰性过期与命中统计。 结构约定:head(哨兵) ... tail(哨兵) 靠近 head = 最近使用;靠近 tail = 最久未用。 双向链表保证节点自带前驱指针,任意位置摘除均为 O(1) (单链表需 O(n) 找前驱,故不可行)。 """ def __init__(self, capacity: int, time_func: Callable[[], float] =...

AI 评审点评

以下是 AI 评审对该模型输出的点评:

【CLAUDE】这是一份高质量的LRU缓存实现,在算法正确性、性能设计和文档质量三个维度均表现出色。核心数据结构设计严谨,哨兵节点、可注入时钟、__slots__等细节体现了工程经验;设计说明深入浅出,权衡分析有理有据;单元测试覆盖全面且用例设计合理。主要不足是并发安全仅有文字说明而无代码示例,但整体而言是一份接近生产级别的参考实现。 【GEMINI】这是一份教科书级别的 LRU 缓存实现。不仅完美满足了所有功能性与非功能性需求,而且在设计决策(如双链表对比、惰性删除权衡、哨兵节点使用、__slots__ 内存优化)的解释上非常深刻。测试用例中引入 FakeClock 解决时间依赖的设计体现了极高的工程素养。 【KIMI】这是一份接近生产级的高质量LRU缓存实现。候选人在算法正确性、性能设计和文档规范三个维度均表现优异,尤其值得称赞的是:通过可注入时钟解决TTL测试的确定性问题、用__slots__优化内存、哨兵节点简化边界处理。主要改进空间在于:(1) warm_up应独立实现避免污染统计计数器;(2) 需补充线程安全实现以满足任务描述的并发安全要求;(3) 可考虑添加__repr__等可观测性接口。整体而言,该实现展现了扎实的数据结构功底和工程化思维。

相关链接

您可以通过以下链接查看更多相关内容:

加载中...