` 机制与 1000 倍加速解析)
科学计算科研数据分析【免费下载链接】astropyAstronomy and astrophysics core library项目地址https://gitcode.com/gh_mirrors/as/astropy点击查看免费下载导读astropy.table.Table是天文数据处理中最常用的容器之一其Table.argsort()与Table.sort()是重排行序的核心接口。本文围绕 astropy 的一项性能优化变更见 变更记录展开新版实现改用col.info.get_sortable_arrays()获取可排序数组替代旧版通过Table.as_array()构建结构化数组的路径使包含Time等 mixin 列的多键排序提速超过 1000 倍。读完本文你将理解get_sortable_arrays()协议的设计思路、Time列双精度儒略日的拆分原理以及argsort()/sort()底层调用链的完整细节。一、问题的起点旧实现为什么慢在优化之前Table.argsort()与Table.sort()通过Table.as_array()把整张表构建成一个 numpy 结构化数组再交给 numpy 统一排序。这条路径存在两个痛点内存开销as_array()需要对所有列做一次拷贝与 dtype 合并构建出结构化数组后再排序多了一步整体数据搬运。mixin 列排序退化当待排键列包含 mixin 列如astropy.time.Time时结构化数组无法直接承载这类对象numpy 会退化为逐元素的对象数组object-array比较。每次比较都要进入 Python 对象层调用Time的__lt__等魔术方法而Time对象本身又包含高精度jd1/jd2双精度分量比较代价极高。包含Time的多键排序因此常常慢得不可接受。变更记录的原文明确指出旧实现“building a structured array viaTable.as_array()”在包含Time的多键排序中会“falling back to slow object-array comparisons”。这正是本次优化的直接动机。二、新机制核心info.get_sortable_arrays()协议2.1 协议定义数据列自我描述排序键优化后的排序逻辑不再由Table统一构造结构化数组而是问每一列要“如何表示自己的排序顺序”。每列的info对象DataInfo/MixinInfo体系需要实现get_sortable_arrays()返回一个或多个可进行字典序排序的 numpy 数组。该协议定义于 astropy/utils/data_info.pydef get_sortable_arrays(self): Return a list of arrays which can be lexically sorted to represent the order of the parent column. The base method raises NotImplementedError and must be overridden. raise NotImplementedError(fcolumn {self.name} is not sortable)基类方法直接抛出NotImplementedError表示“该列没有提供可排序表示”。这带来一个清晰的错误边界任何不支持该协议的列在排序时会得到明确的报错而不是静默走慢速路径。2.2 普通列返回列数据本身对普通的Column数值、字符串等原生 dtype排序键就是列数据本身实现见 astropy/table/column.pydef get_sortable_arrays(self) - list[npt.NDArray]: col self._parent # For a structured array return each of the individual fields in order if col.dtype.names: return [col[name] for name in col.dtype.names] return [self._parent]值得注意的细节如果列本身是结构化数组col.dtype.names非空则按字段顺序展开为多个数组保证字典序与字段顺序一致。2.3Time列双精度儒略日的无损拆分Time是本次优化的重头戏。Time内部用jd1jd2两个 float64 表示高精度儒略日总精度约 20 位有效数字。旧实现若要排序只能退化为对象数组逐元素比较。新的实现见 astropy/time/core.pydef get_sortable_arrays(self): parent self._parent jd1, jd2 parent.jd1, parent.jd2 jd_approx jd1 jd2 jd_remainder (jd1 - jd_approx) jd2 return [jd_approx, jd_remainder]其原理与Time.argsort()astropy/time/core.py一致jd_approx jd1 jd2得到浮点精度内可表达的最佳近似值jd_remainder (jd1 - jd_approx) jd2通过(jd1 - approx)的补偿计算还原被舍去的低阶余量返回[jd_approx, jd_remainder]两个普通 float 数组两者做字典序排序即可无损恢复完整 20 位精度的时间顺序。关键在于这两个数组都是普通 float64 数组numpy 可以用纯 C 层快速比较完全绕开 Python 对象层的Time比较。变更记录特别提到Time.info.get_sortable_arrays()本身也被提速——旧版需要临时构造中间Time对象再做减法新版直接基于jd1/jd2裸数组做浮点拆分避免了对象构造开销。三、argsort()的完整调用链新版Table.argsort()实现位于 astropy/table/table.py关键流程如下索引捷径若键列存在表索引get_index(self, nameskeys)非空直接返回index.sorted_data()完全绕过排序计算。收集排序键遍历每个键列调用self[key].info.get_sortable_arrays()并把结果平铺进sortable_arrays列表sortable_arrays [] for key in key_names: try: sortable_arrays.extend(self[key].info.get_sortable_arrays()) except NotImplementedError as err: raise TypeError(fcolumn {key!r} is not sortable) from errNotImplementedError会被转换为更友好的TypeError并明确指出不可排序的列名。 3.执行排序若只有一个可排序数组用np.argsort(sortable_arrays[0], kindkind)此时kindquicksort/mergesort/heapsort/stable参数生效若有多个数组使用np.lexsort(sortable_arrays[::-1])。这里有一个易错点np.lexsort把最后一个参数当作主排序键而get_sortable_arrays()返回的数组是从主到次排列的因此必须先反转顺序。reverse 处理reverseTrue时对结果索引取反idx[::-1]。四、sort()的原地重排Table.sort()astropy/table/table.py本身不做排序计算而是复用argsort()indexes self.argsort(keys, kindkind, reversereverse) with self.index_mode(freeze): for col in self.columns.values(): new_col col.take(indexes, axis0) try: col[:] new_col except Exception: self[col.info.name] new_col要点所有列统一按argsort得到的索引执行col.take(indexes, axis0)重排保证行间对齐排序过程在index_mode(freeze)上下文内进行避免索引在逐列重排过程中被反复更新若列支持原地更新col[:] new_col则原位赋值否则整体替换为新列——这是为兼容不支持原地更新的 mixin 类所做的回退详见 issue #9553、#9536 的讨论当keysNone时sort()要求表存在索引否则抛出ValueError(Table sort requires input keys or a table index)。五、性能收益数字与适用场景变更记录给出的量化结论包含Time的 mixin 列多键排序提速超过 1000 倍。原因是彻底消除了对象数组比较路径——Time列的每次比较从创建/比较Time对象内部含两个 float64变成直接对两个 float64 做 C 层字典序比较普通多列排序快数倍。原因是省去了as_array()构建结构化数组的搬运成本直接对各键列的可排序数组多数情况下就是列数据自身做lexsort。需要说明的是这些数字来自该变更记录的报告实际收益与数据规模、键列数量、Time列占比相关但“避免对象比较 减少一次全表拷贝”这两条优化路径的收益方向是明确的。六、测试验证与回归保护本次优化有对应的测试覆盖见 astropy/table/tests/test_operations.pytest_argsort_time_column回归测试 #10823验证t.argsort(time)与times.argsort()结果一致test_argsort_multiple_keys_with_time_column回归测试 gh-14942验证Time与其他键列混合排序[time, idx]与[idx, time]两种主次顺序均得到正确索引该测试的 docstring 明确记录了“Table.argsort()使用col.info.get_sortable_arrays()使Time等 mixin 列高效正确排序”这一设计意图test_argsort_non_sortable_mixin_raises验证不实现该协议的列如SkyCoord会抛出TypeError: column sc is not sortable。测试同时覆盖了Table.sort()对已建索引表的排序test_sort_indexed_table确保新实现不破坏索引路径。七、实战使用示例日常使用中API 完全不变无需改代码即可享受性能收益from astropy.table import Table from astropy.time import Time t Table({ time: Time([2018-01-01, 2018-01-01, 2017-01-01, 2016-01-01]), idx: [1, 0, 1, 0], }) # 多键排序先按 time再按 idx结果索引为 [3, 2, 1, 0] i t.argsort([time, idx]) # 原地排序先按 idx 再按 time t.sort([idx, time]) # 多键排序中 kind 参数仅在单数组时生效 # 含 Time 等展开为多数组的列时会用 np.lexsortkind 被忽略官方文档对排序用法的说明见 docs/table/modify_table.rstt.sort(b)、t.sort([a, b])。八、总结get_sortable_arrays()协议把“如何表示排序键”的决策权从Table下沉到各列类型自身实现了三层收益正确性Time的双精度儒略日通过近似值 余数的双数组拆分得到无损表示字典序与真实时间序严格一致性能所有排序键最终都是普通 numpy 数组全程 C 层快速路径规避对象数组比较多键排序收益可达千倍量级可扩展性任何新 mixin 类型只需实现info.get_sortable_arrays()即可获得高效排序未实现者得到明确的TypeError而非静默降级。对于需要频繁对含时间列的大型表做多键排序的天文数据处理流水线而言这是性价比极高的一项底层优化——接口零变化收益直接到账。赞分享科学计算科研数据分析【免费下载链接】astropyAstronomy and astrophysics core library项目地址https://gitcode.com/gh_mirrors/as/astropy点击查看免费下载相关推荐从入门到精通convformer_s36.sail_in1k图像分类模型快速上手指南从入门到精通convformer_s36.sail_in1k图像分类模型快速上手指南 想要在图像分类任务上快速获得专业级效果吗convformer_s36.10倍提速TiDB排序查询ORDER BY性能优化指南10倍提速TiDB排序查询ORDER BY性能优化指南 你是否遇到过TiDB执行 ORDER BY 时全表扫描导致查询超时是否疑惑为什么相同SQL在MySQ数据库分布式数据库后端OLAPAstropy 单位字符串解析缓存机制Unit(km/s) 性能优化详解Astropy 单位字符串解析缓存机制 Unit km/s 性能优化详解 导读 本文围绕 Astropy 仓库中的性能改进记录 20090.perf.rs科学计算科研数据分析上一篇在 Windows 上使用 WinGet 安装 git-cliff从包管理器到生成首个 Changelog 的完整指南下一篇Better Auth SSO 插件实战指南用 SAML 2.0 与 OIDC 为应用接入企业级单点登录创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考