Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-18 - Fast `to_dict` serialization for `RequestMetrics`
**Learning:** `dataclasses.asdict` is extremely slow because it performs deep copies and recursively serializes nested components. By using custom reflection (`__dataclass_fields__`) combined with primitive checking and direct method delegation, object serialization overhead can be significantly reduced (~2x). This is especially critical on high-throughput hot paths like request metrics reporting in API servers/scheduler instances. In addition, avoiding inline imports (e.g., `import dataclasses` inside `to_dict`) is crucial as dictionary lookup on `sys.modules` adds overhead to tight loops.
**Action:** Consider custom `to_dict()` methods over `asdict()` for heavily instantiated dataclasses on hot execution paths, and always put imports at module scope.
24 changes: 23 additions & 1 deletion fastdeploy/engine/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from __future__ import annotations

import dataclasses
import json
import time
import traceback
Expand Down Expand Up @@ -897,7 +898,28 @@ def to_dict(self):
"""
Convert the RequestMetrics object to a dictionary.
"""
return {k: v for k, v in asdict(self).items()}
# Custom serialization is significantly faster than dataclasses.asdict()
res = {}
for k in self.__dataclass_fields__:
v = getattr(self, k)
if type(v) in (int, float, str, bool, type(None)):
res[k] = v
else:
if dataclasses.is_dataclass(v):
res[k] = v.to_dict() if hasattr(v, "to_dict") else dataclasses.asdict(v)
elif isinstance(v, list):
res[k] = [
(x.to_dict() if hasattr(x, "to_dict") else dataclasses.asdict(x) if dataclasses.is_dataclass(x) else x)
for x in v
]
elif isinstance(v, dict):
res[k] = {
key: (val.to_dict() if hasattr(val, "to_dict") else dataclasses.asdict(val) if dataclasses.is_dataclass(val) else val)
Comment on lines +909 to +917
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里通过 hasattr(x, "to_dict") 判断后直接调用,若对象恰好有同名非可调用属性会在运行时报 TypeError。建议改为 callable(getattr(x, "to_dict", None))v/list 元素/dict 值都一致处理),避免属性遮蔽导致的序列化失败。

Suggested change
res[k] = v.to_dict() if hasattr(v, "to_dict") else dataclasses.asdict(v)
elif isinstance(v, list):
res[k] = [
(x.to_dict() if hasattr(x, "to_dict") else dataclasses.asdict(x) if dataclasses.is_dataclass(x) else x)
for x in v
]
elif isinstance(v, dict):
res[k] = {
key: (val.to_dict() if hasattr(val, "to_dict") else dataclasses.asdict(val) if dataclasses.is_dataclass(val) else val)
res[k] = v.to_dict() if callable(getattr(v, "to_dict", None)) else dataclasses.asdict(v)
elif isinstance(v, list):
res[k] = [
(x.to_dict() if callable(getattr(x, "to_dict", None)) else dataclasses.asdict(x) if dataclasses.is_dataclass(x) else x)
for x in v
]
elif isinstance(v, dict):
res[k] = {
key: (val.to_dict() if callable(getattr(val, "to_dict", None)) else dataclasses.asdict(val) if dataclasses.is_dataclass(val) else val)

Copilot uses AI. Check for mistakes.
for key, val in v.items()
}
else:
Comment on lines +910 to +920
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

当前 list/dict 分支使用了较长的嵌套三元表达式,可读性和后续扩展性较差,也不利于单元测试覆盖边界类型。建议提取一个小的私有辅助函数(例如 _serialize_value(val))统一处理 dataclass / to_dict / 容器递归逻辑,主循环只做 res[k] = _serialize_value(v)

Copilot uses AI. Check for mistakes.
res[k] = v
return res
Comment on lines +901 to +922
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

目前 RequestMetrics.to_dict() 做了加速实现,但同文件 Request.to_dict() 仍在用 asdict(self.metrics)(见 request.py:490-492),会继续触发深拷贝开销,可能抵消本次优化在主路径上的收益。建议将该处改为 self.metrics.to_dict()(或统一使用新的序列化逻辑),确保热点路径真正受益。

Copilot uses AI. Check for mistakes.

def record_recv_first_token(self):
cur_time = time.time()
Expand Down
Loading