1- import warnings
21import functools
2+ import warnings
3+
34import torch
45
5- from . import backend , common , error
6- from .backend .device import DeviceDetector
76from ..logging_utils import (
8- precision_config ,
7+ compare_outputs ,
98 get_precision_logger ,
109 get_tensor_info ,
11- get_call_location ,
12- compare_outputs ,
10+ precision_config ,
1311)
12+ from . import backend , common , error
13+ from .backend .device import DeviceDetector
1414
1515
1616class Register :
@@ -34,7 +34,6 @@ def __init__(
3434 self .all_ops = []
3535 self .all_keys = []
3636
37-
3837 # optional mapping func_name -> list of config entries
3938 self .full_config_by_func = full_config_by_func
4039 self .cpp_patched_ops = set (cpp_patched_ops or [])
@@ -144,27 +143,13 @@ def get_vendor_name(self):
144143 def get_current_device (self ):
145144 return self .device .name
146145
147- # def _wrap_op_with_precision_check(op_key, fn):
148- # @functools.wraps(fn)
149- # def wrapper(*args, **kwargs):
150- # fg_result = fn(*args, **kwargs)
151-
152- # cfg = precision_config
153- # if cfg["enabled"] and op_key not in cfg["logged_ops"]:
154- # cfg["logged_ops"].add(op_key)
155- # logger = get_precision_logger()
156- # input_info = [get_tensor_info(a) for a in args if get_tensor_info(a)]
157- # logger.info(f"Op: {op_key} | in: {input_info}")
158-
159- # return fg_result
160- # return wrapper
161-
162- # 精度检查时允许的最大 tensor 元素数(超过则跳过,避免大 tensor 拷贝开销)
163- _MAX_NUMEL_FOR_CHECK = 1 * 1024 * 1024 # 1M 元素
146+
147+ # Maximum tensor element count allowed for precision check (skip if exceeded to avoid large tensor copy overhead)
148+ _MAX_NUMEL_FOR_CHECK = 1 * 1024 * 1024 # 1M elements
164149
165150
166151def _get_dtype_tolerance (args , default_rtol , default_atol ):
167- """根据输入 tensor 的 dtype 自动调整容差 """
152+ """Automatically adjust tolerance based on the dtype of input tensors """
168153 for a in args :
169154 if isinstance (a , torch .Tensor ) and a .is_floating_point ():
170155 if a .dtype in (torch .bfloat16 , torch .float16 ):
@@ -174,7 +159,7 @@ def _get_dtype_tolerance(args, default_rtol, default_atol):
174159
175160
176161def _to_cpu (x ):
177- """递归地将 tensor 移到 CPU"""
162+ """Recursively move tensors to CPU"""
178163 if isinstance (x , torch .Tensor ):
179164 return x .detach ().cpu ()
180165 elif isinstance (x , (list , tuple )):
@@ -185,7 +170,7 @@ def _to_cpu(x):
185170
186171
187172def _max_tensor_numel (args ):
188- """返回参数中最大 tensor 的元素数 """
173+ """Return the element count of the largest tensor in the arguments """
189174 max_n = 0
190175 for a in args :
191176 if isinstance (a , torch .Tensor ):
@@ -194,65 +179,88 @@ def _max_tensor_numel(args):
194179
195180
196181def _wrap_op_with_precision_check (op_key , fn ):
197- """包装 FlagGems 算子,在执行后与 PyTorch 原生 CPU 实现做精度对比。
182+ """Wrap a FlagGems operator to compare its output against the native PyTorch CPU implementation after execution.
198183
199- 由于 FlagGems 替换了 CUDA dispatch,无法在 GPU 上调用原生实现,
200- 因此将输入拷贝到 CPU 计算参考结果。通过以下方式控制性能开销:
201- - max_checks: 每个算子只检查前 N 次调用(默认 10)
202- - 跳过大 tensor(超过 1M 元素)
203- - 一旦记录过失败就不再检查该算子
184+ Since FlagGems replaces the CUDA dispatch, the native implementation cannot be called on GPU,
185+ so inputs are copied to CPU to compute the reference result. Performance overhead is controlled by:
186+ - max_checks: only check the first N calls per operator (default 10)
187+ - skip large tensors (over 1M elements)
188+ - once a failure is logged, that operator is no longer checked
204189 """
205190 _call_count = 0
206191
207192 @functools .wraps (fn )
208193 def wrapper (* args , ** kwargs ):
209194 nonlocal _call_count
210195
211- # 先执行 FlagGems 实现
196+ # Execute the FlagGems implementation first
212197 fg_result = fn (* args , ** kwargs )
213198
214199 cfg = precision_config
215200 if not cfg ["enabled" ]:
216201 return fg_result
217202
218- # 已经记录过失败的算子不再检查
203+ # Skip operators that have already logged a failure
219204 if op_key in cfg ["logged_ops" ]:
220205 return fg_result
221206
222- # 采样:每个算子只检查前 N 次调用
207+ # Sampling: only check the first N calls per operator
223208 _call_count += 1
224209 if _call_count > cfg .get ("max_checks" , 10 ):
225210 return fg_result
226211
227- op_name = op_key .split ("::" )[- 1 ].split ("." )[0 ] if "::" in op_key else op_key .split ("." )[0 ]
212+ op_name = (
213+ op_key .split ("::" )[- 1 ].split ("." )[0 ]
214+ if "::" in op_key
215+ else op_key .split ("." )[0 ]
216+ )
228217
229- # 跳过 out variant(参数签名不同,CPU 调用容易出错)
218+ # Skip out variants (different argument signatures, prone to errors when calling on CPU)
230219 overload_part = op_key .split ("." )[- 1 ] if "." in op_key else ""
231220 if overload_part == "out" or op_name .endswith ("_out" ):
232221 return fg_result
233222
234- # 跳过不需要检查的算子
223+ # Skip operators that do not need checking
235224 skip_ops = {
236- # 纯 layout / 内存操作
237- 'copy_' , '_to_copy' , 'view' , 'reshape' , 'expand' , 'permute' ,
238- 'transpose' , 'contiguous' , 'clone' , 'to' , 'empty' , 'zeros' ,
239- 'ones' , 'full' , 'masked_fill_' ,
240- # 随机采样算子(GPU/CPU 随机数生成器不同,结果必然不一致)
241- 'exponential_' , 'normal_' , 'uniform_' , 'bernoulli_' , 'random_' ,
242- 'multinomial' , 'randperm' ,
243- # 排序/选择算子(相同值的排序顺序可能不同,不适合逐元素比较)
244- 'sort' , 'topk' , 'argsort' ,
225+ # Pure layout / memory operations
226+ "copy_" ,
227+ "_to_copy" ,
228+ "view" ,
229+ "reshape" ,
230+ "expand" ,
231+ "permute" ,
232+ "transpose" ,
233+ "contiguous" ,
234+ "clone" ,
235+ "to" ,
236+ "empty" ,
237+ "zeros" ,
238+ "ones" ,
239+ "full" ,
240+ "masked_fill_" ,
241+ # Random sampling operators (GPU/CPU RNGs differ, results will inevitably mismatch)
242+ "exponential_" ,
243+ "normal_" ,
244+ "uniform_" ,
245+ "bernoulli_" ,
246+ "random_" ,
247+ "multinomial" ,
248+ "randperm" ,
249+ # Sorting/selection operators (order of equal values may differ, not suitable for element-wise comparison)
250+ "sort" ,
251+ "topk" ,
252+ "argsort" ,
245253 }
246254 if op_name in skip_ops :
247255 return fg_result
248256
249- # 跳过大 tensor,避免拷贝开销
257+ # Skip large tensors to avoid copy overhead
250258 if _max_tensor_numel (args ) > _MAX_NUMEL_FOR_CHECK :
251259 return fg_result
252260
253261 try :
254- # 解析 op_key 获取正确的 overload
255- # op_key 形如 "add.Tensor", "mm.default", "softmax.int" 等
262+ # Parse op_key to get the correct overload
263+ # op_key is in the form "add.Tensor", "mm.default", "softmax.int", etc.
256264 parts = op_key .split ("." )
257265 base_name = parts [0 ]
258266 overload_name = parts [1 ] if len (parts ) > 1 else "default"
@@ -264,17 +272,17 @@ def wrapper(*args, **kwargs):
264272 if aten_overload is None :
265273 return fg_result
266274
267- # 将输入拷贝到 CPU,调用原生 aten 实现(CPU 上不受 FlagGems 影响)
275+ # Copy inputs to CPU and call the native aten implementation (unaffected by FlagGems on CPU)
268276 cpu_args = [_to_cpu (a ) for a in args ]
269277 cpu_kwargs = {k : _to_cpu (v ) for k , v in kwargs .items ()}
270278
271279 with torch .no_grad ():
272280 pt_result_cpu = aten_overload (* cpu_args , ** cpu_kwargs )
273281
274- # 将 FlagGems 结果也拷贝到 CPU 做比较(避免把 CPU 结果搬回 GPU)
282+ # Also copy FlagGems result to CPU for comparison (avoid moving CPU result back to GPU)
275283 fg_result_cpu = _to_cpu (fg_result )
276284
277- # 根据 dtype 自动调整容差
285+ # Automatically adjust tolerance based on dtype
278286 rtol , atol = _get_dtype_tolerance (args , cfg ["rtol" ], cfg ["atol" ])
279287
280288 is_close , info = compare_outputs (fg_result_cpu , pt_result_cpu , rtol , atol )
@@ -300,6 +308,7 @@ def wrapper(*args, **kwargs):
300308
301309 return wrapper
302310
311+
303312class PrecisionCheckRegister (Register ):
304313 def register_impl (self , key , fn ):
305314 if self .lib is None :
0 commit comments