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 @@
## 2025-03-29 - Optimize dataclass serialization for metrics
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

仓库 PR 模板要求标题至少包含一个 tag(如 [Optimization]、[Engine] 等);当前 PR 标题未包含方括号 tag。建议更新标题以符合模板约定,方便后续变更分类与发布记录。

Suggested change
## 2025-03-29 - Optimize dataclass serialization for metrics
## [Optimization] 2025-03-29 - Optimize dataclass serialization for metrics

Copilot uses AI. Check for mistakes.
**Learning:** `dataclasses.asdict()` relies on recursive deepcopying which introduces significant overhead, especially for objects created and serialized frequently on the hot path (like `RequestMetrics` per request).
**Action:** Replace `asdict()` with manual `to_dict()` methods that iterate over `__dataclass_fields__` using `getattr()`. Explicitly copy primitives, shallow copy lists/dicts, and call `.to_dict()` on nested dataclasses (like `SpeculateMetrics`) to avoid deepcopy overhead while maintaining the correct dictionary structure.
19 changes: 18 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,23 @@ def to_dict(self):
"""
Convert the RequestMetrics object to a dictionary.
"""
return {k: v for k, v in asdict(self).items()}
res = {}
for k in self.__dataclass_fields__:
v = getattr(self, k)
if type(v) in (int, float, str, bool, type(None)):
res[k] = v
elif dataclasses.is_dataclass(v):
if hasattr(v, "to_dict"):
res[k] = v.to_dict()
else:
res[k] = dataclasses.asdict(v)
elif isinstance(v, list):
res[k] = list(v)
elif isinstance(v, dict):
res[k] = dict(v)
else:
res[k] = v
return res
Comment on lines +901 to +917
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

当前虽然优化了 RequestMetrics.to_dict(),但仓库内仍存在对 RequestMetrics 使用 dataclasses.asdict() 的序列化路径(例如 Request.to_dict() 里 data.update(asdict(self.metrics)))。这会继续触发递归 deepcopy,导致本次优化在部分场景下收益被抵消;建议将这些调用点改为 metrics.to_dict()(或至少避免对 RequestMetrics 调用 asdict)。

Copilot uses AI. Check for mistakes.

def record_recv_first_token(self):
cur_time = time.time()
Expand Down
13 changes: 13 additions & 0 deletions fastdeploy/worker/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,19 @@ class SpeculateMetrics:
"""
accept_ratio_per_head: list[float]

def to_dict(self):
"""
convert SpeculateMetrics to a serialized dict
"""
return {
"accepted_tokens": self.accepted_tokens,
"rejected_tokens": self.rejected_tokens,
"accept_ratio": self.accept_ratio,
"average_accept_length": self.average_accept_length,
"accepted_tokens_per_head": list(self.accepted_tokens_per_head),
"accept_ratio_per_head": list(self.accept_ratio_per_head),
}
Comment on lines +167 to +178
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

新增 SpeculateMetrics.to_dict() 后建议补充对应单测(例如构造 SpeculateMetrics 并断言 to_dict() 输出字段与 dataclass 字段一致、list 字段被正确序列化),避免后续字段调整或类型变化时出现静默不兼容。

Copilot generated this review using guidance from repository custom instructions.


@dataclass
class SamplerOutput:
Expand Down
Loading