l0n0lc 0.8.4__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- l0n0lc/Py/350/275/254Cpp/350/275/254/350/257/221/345/231/250.py +579 -0
- l0n0lc/__init__.py +75 -6
- l0n0lc/aot/347/274/226/350/257/221.py +679 -0
- l0n0lc/ast/350/256/277/351/227/256/350/200/205.py +599 -0
- l0n0lc/cpp/347/261/273/345/236/213.py +330 -0
- l0n0lc/cpp/347/274/226/350/257/221/345/231/250.py +317 -0
- l0n0lc/simd/344/274/230/345/214/226.py +260 -0
- l0n0lc/std_map.py +153 -0
- l0n0lc/std_set.py +185 -0
- l0n0lc/std_vector.py +96 -0
- l0n0lc//344/273/243/347/240/201/344/274/230/345/214/226.py +302 -0
- l0n0lc//344/273/243/347/240/201/347/224/237/346/210/220.py +546 -0
- l0n0lc//344/276/235/350/265/226/346/263/250/345/205/245.py +155 -0
- l0n0lc//345/215/263/346/227/266/347/274/226/350/257/221.py +192 -0
- l0n0lc//345/217/230/351/207/217/347/256/241/347/220/206/345/231/250.py +123 -0
- l0n0lc//345/237/272/347/241/200/346/230/240/345/260/204.py +103 -0
- l0n0lc//345/237/272/347/241/200/346/267/267/345/205/245.py +147 -0
- l0n0lc//345/256/271/345/231/250/346/236/204/345/273/272/345/231/250.py +214 -0
- l0n0lc//345/267/245/345/205/267.py +285 -0
- l0n0lc//345/271/266/350/241/214/347/274/226/350/257/221/345/231/250.py +412 -0
- l0n0lc//345/274/202/345/270/270.py +474 -0
- l0n0lc//346/225/260/347/273/204/345/257/271/350/261/241/346/261/240.py +248 -0
- l0n0lc//346/226/207/344/273/266/347/256/241/347/220/206/345/231/250.py +286 -0
- l0n0lc//346/227/245/345/277/227/345/267/245/345/205/267.py +152 -0
- l0n0lc//347/261/273/345/236/213/346/216/250/346/226/255/345/267/245/345/205/267.py +352 -0
- l0n0lc//347/261/273/345/236/213/350/275/254/346/215/242.py +210 -0
- l0n0lc//347/261/273/346/224/257/346/214/201.py +372 -0
- l0n0lc//347/274/226/350/257/221/344/270/212/344/270/213/346/226/207.py +132 -0
- l0n0lc//347/274/226/350/257/221/347/256/241/347/220/206/345/231/250.py +171 -0
- l0n0lc//350/241/250/350/276/276/345/274/217/345/244/204/347/220/206.py +462 -0
- l0n0lc//350/275/254/350/257/221/345/231/250/345/267/245/345/205/267.py +49 -0
- l0n0lc//350/277/220/350/241/214/346/227/266/345/212/240/350/275/275.py +217 -0
- l0n0lc//351/200/232/347/224/250/345/267/245/345/205/267.py +149 -0
- l0n0lc-1.0.0.dist-info/METADATA +363 -0
- l0n0lc-1.0.0.dist-info/RECORD +39 -0
- {l0n0lc-0.8.4.dist-info → l0n0lc-1.0.0.dist-info}/WHEEL +1 -1
- l0n0lc-1.0.0.dist-info/entry_points.txt +2 -0
- {l0n0lc-0.8.4.dist-info → l0n0lc-1.0.0.dist-info}/licenses/LICENSE +0 -0
- l0n0lc/StdList.py +0 -24
- l0n0lc/StdMap.py +0 -21
- l0n0lc/c/345/237/272/347/241/200/345/244/204/347/220/206.py +0 -207
- l0n0lc/jit.py +0 -604
- l0n0lc//347/274/226/350/257/221.py +0 -58
- l0n0lc//351/200/232/347/224/250.py +0 -134
- l0n0lc-0.8.4.dist-info/METADATA +0 -241
- l0n0lc-0.8.4.dist-info/RECORD +0 -12
- {l0n0lc-0.8.4.dist-info → l0n0lc-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AOT (Ahead-Of-Time) 预编译模块
|
|
3
|
+
|
|
4
|
+
允许用户在运行前批量预编译所有 @jit 装饰的函数,
|
|
5
|
+
从而消除首次调用时的编译延迟。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import ast
|
|
10
|
+
import sys
|
|
11
|
+
import argparse
|
|
12
|
+
import inspect
|
|
13
|
+
import importlib.util
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import List, Dict, Set, Tuple, Optional, Callable, Any
|
|
16
|
+
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
17
|
+
|
|
18
|
+
from .Py转Cpp转译器 import Py转Cpp转译器
|
|
19
|
+
from .cpp编译器 import Cpp编译器
|
|
20
|
+
from .工具 import 全局上下文
|
|
21
|
+
from .日志工具 import 日志, 日志级别
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class JIT函数信息:
|
|
25
|
+
"""存储 JIT 函数的元信息"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
函数名: str,
|
|
30
|
+
函数对象: Callable,
|
|
31
|
+
模块名: str,
|
|
32
|
+
源文件路径: str,
|
|
33
|
+
行号: int,
|
|
34
|
+
装饰器参数: Optional[Dict[str, Any]] = None
|
|
35
|
+
):
|
|
36
|
+
self.函数名 = 函数名
|
|
37
|
+
self.函数对象 = 函数对象
|
|
38
|
+
self.模块名 = 模块名
|
|
39
|
+
self.源文件路径 = 源文件路径
|
|
40
|
+
self.行号 = 行号
|
|
41
|
+
self.装饰器参数 = 装饰器参数 or {}
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return f"JIT函数信息({self.模块名}.{self.函数名} @ {self.源文件路径}:{self.行号})"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class 包扫描器:
|
|
48
|
+
"""
|
|
49
|
+
扫描 Python 包,提取所有 @jit 装饰的函数
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, 根目录: str):
|
|
53
|
+
"""
|
|
54
|
+
初始化包扫描器
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
根目录: 要扫描的根目录(包的根目录)
|
|
58
|
+
"""
|
|
59
|
+
self.根目录 = Path(根目录).resolve()
|
|
60
|
+
if not self.根目录.exists():
|
|
61
|
+
raise ValueError(f"目录不存在: {根目录}")
|
|
62
|
+
|
|
63
|
+
def 扫描目录(
|
|
64
|
+
self,
|
|
65
|
+
递归: bool = True,
|
|
66
|
+
排除目录: Optional[Set[str]] = None,
|
|
67
|
+
包含测试: bool = False
|
|
68
|
+
) -> List[JIT函数信息]:
|
|
69
|
+
"""
|
|
70
|
+
扫描目录下所有 Python 文件,提取 JIT 函数
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
递归: 是否递归扫描子目录
|
|
74
|
+
排除目录: 要排除的目录名集合
|
|
75
|
+
包含测试: 是否包含测试文件(test_*.py)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
JIT 函数信息列表
|
|
79
|
+
"""
|
|
80
|
+
排除目录 = 排除目录 or {
|
|
81
|
+
"__pycache__",
|
|
82
|
+
".git",
|
|
83
|
+
".venv",
|
|
84
|
+
"venv",
|
|
85
|
+
"env",
|
|
86
|
+
"build",
|
|
87
|
+
"dist",
|
|
88
|
+
".eggs",
|
|
89
|
+
"*.egg-info",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
jit函数列表 = []
|
|
93
|
+
|
|
94
|
+
# 查找所有 Python 文件
|
|
95
|
+
模式 = "**/*.py" if 递归 else "*.py"
|
|
96
|
+
for py文件 in self.根目录.rglob(模式):
|
|
97
|
+
# 检查是否应该跳过此文件
|
|
98
|
+
if not self._应该包含文件(py文件, 排除目录, 包含测试):
|
|
99
|
+
continue
|
|
100
|
+
|
|
101
|
+
# 从文件中提取 JIT 函数
|
|
102
|
+
文件函数 = self._扫描文件(str(py文件))
|
|
103
|
+
jit函数列表.extend(文件函数)
|
|
104
|
+
|
|
105
|
+
日志.信息(f"扫描完成,找到 {len(jit函数列表)} 个 JIT 函数")
|
|
106
|
+
return jit函数列表
|
|
107
|
+
|
|
108
|
+
def _应该包含文件(
|
|
109
|
+
self,
|
|
110
|
+
文件路径: Path,
|
|
111
|
+
排除目录: Set[str],
|
|
112
|
+
包含测试: bool
|
|
113
|
+
) -> bool:
|
|
114
|
+
"""判断文件是否应该被扫描"""
|
|
115
|
+
# 检查文件名
|
|
116
|
+
if not 包含测试 and 文件路径.name.startswith("test_"):
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
# 检查父目录是否在排除列表中
|
|
120
|
+
for 父目录 in 文件路径.parents:
|
|
121
|
+
if 父目录.name in 排除目录:
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
def _扫描文件(self, 文件路径: str) -> List[JIT函数信息]:
|
|
127
|
+
"""
|
|
128
|
+
扫描单个 Python 文件,提取 JIT 函数
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
文件路径: Python 文件路径
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
JIT 函数信息列表
|
|
135
|
+
"""
|
|
136
|
+
try:
|
|
137
|
+
with open(文件路径, "r", encoding="utf-8") as f:
|
|
138
|
+
源代码 = f.read()
|
|
139
|
+
except Exception as e:
|
|
140
|
+
日志.警告(f"无法读取文件 {文件路径}: {e}")
|
|
141
|
+
return []
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
树 = ast.parse(源代码, filename=文件路径)
|
|
145
|
+
except SyntaxError as e:
|
|
146
|
+
日志.警告(f"文件 {文件路径} 语法错误: {e}")
|
|
147
|
+
return []
|
|
148
|
+
|
|
149
|
+
jit函数列表 = []
|
|
150
|
+
模块名 = Path(文件路径).stem
|
|
151
|
+
|
|
152
|
+
for 节点 in ast.walk(树):
|
|
153
|
+
if isinstance(节点, ast.FunctionDef):
|
|
154
|
+
# 检查是否使用了 @jit 或 @即时编译 装饰器
|
|
155
|
+
if self._是jit函数(节点):
|
|
156
|
+
# 尝试从实际模块中导入函数对象
|
|
157
|
+
函数对象 = self._导入函数(文件路径, 节点.name)
|
|
158
|
+
|
|
159
|
+
装饰器参数 = self._提取装饰器参数(节点)
|
|
160
|
+
|
|
161
|
+
信息 = JIT函数信息(
|
|
162
|
+
函数名=节点.name,
|
|
163
|
+
函数对象=函数对象, # type: ignore
|
|
164
|
+
模块名=模块名,
|
|
165
|
+
源文件路径=文件路径,
|
|
166
|
+
行号=节点.lineno,
|
|
167
|
+
装饰器参数=装饰器参数
|
|
168
|
+
)
|
|
169
|
+
jit函数列表.append(信息)
|
|
170
|
+
|
|
171
|
+
return jit函数列表
|
|
172
|
+
|
|
173
|
+
def _是jit函数(self, 函数节点: ast.FunctionDef) -> bool:
|
|
174
|
+
"""检查函数是否使用了 @jit 或 @即时编译 装饰器"""
|
|
175
|
+
for 装饰器 in 函数节点.decorator_list:
|
|
176
|
+
# 检查 @jit
|
|
177
|
+
if isinstance(装饰器, ast.Name) and 装饰器.id in ("jit", "即时编译"):
|
|
178
|
+
return True
|
|
179
|
+
# 检查 @lc.jit() 形式
|
|
180
|
+
if isinstance(装饰器, ast.Call):
|
|
181
|
+
if isinstance(装饰器.func, ast.Attribute):
|
|
182
|
+
if 装饰器.func.attr in ("jit", "即时编译"):
|
|
183
|
+
return True
|
|
184
|
+
elif isinstance(装饰器.func, ast.Name):
|
|
185
|
+
if 装饰器.func.id in ("jit", "即时编译"):
|
|
186
|
+
return True
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
def _提取装饰器参数(
|
|
190
|
+
self, 函数节点: ast.FunctionDef
|
|
191
|
+
) -> Dict[str, Any]:
|
|
192
|
+
"""
|
|
193
|
+
从装饰器中提取参数
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
装饰器参数字典
|
|
197
|
+
"""
|
|
198
|
+
参数 = {}
|
|
199
|
+
|
|
200
|
+
for 装饰器 in 函数节点.decorator_list:
|
|
201
|
+
if isinstance(装饰器, ast.Call):
|
|
202
|
+
尝试从调用提取参数(装饰器, 参数)
|
|
203
|
+
|
|
204
|
+
return 参数
|
|
205
|
+
|
|
206
|
+
def _导入函数(self, 文件路径: str, 函数名: str) -> Optional[Callable]:
|
|
207
|
+
"""
|
|
208
|
+
从文件中导入函数对象
|
|
209
|
+
|
|
210
|
+
注意:这可能需要正确设置 Python 路径
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
# 获取文件的目录和模块名
|
|
214
|
+
文件目录 = os.path.dirname(文件路径)
|
|
215
|
+
模块名 = Path(文件路径).stem
|
|
216
|
+
|
|
217
|
+
# 添加目录到 sys.path
|
|
218
|
+
原路径 = sys.path[:]
|
|
219
|
+
if 文件目录 not in sys.path:
|
|
220
|
+
sys.path.insert(0, 文件目录)
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
# 动态导入模块
|
|
224
|
+
spec = importlib.util.spec_from_file_location(模块名, 文件路径)
|
|
225
|
+
if spec and spec.loader:
|
|
226
|
+
模块 = importlib.util.module_from_spec(spec)
|
|
227
|
+
spec.loader.exec_module(模块)
|
|
228
|
+
|
|
229
|
+
# 获取函数对象(可能是 Py转Cpp转译器 对象)
|
|
230
|
+
if hasattr(模块, 函数名):
|
|
231
|
+
对象 = getattr(模块, 函数名)
|
|
232
|
+
# 如果是 JIT 装饰器返回的转译器对象,提取原始函数
|
|
233
|
+
if isinstance(对象, Py转Cpp转译器):
|
|
234
|
+
return 对象.目标函数
|
|
235
|
+
return 对象
|
|
236
|
+
finally:
|
|
237
|
+
# 恢复 sys.path
|
|
238
|
+
sys.path[:] = 原路径
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
日志.调试(f"无法导入函数 {文件路径}:{函数名}: {e}")
|
|
242
|
+
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def 尝试从调用提取参数(装饰器: ast.Call, 参数字典: Dict[str, Any]):
|
|
247
|
+
"""尝试从装饰器调用中提取参数"""
|
|
248
|
+
for 关键字 in 装饰器.keywords:
|
|
249
|
+
if isinstance(关键字.value, ast.Constant):
|
|
250
|
+
参数字典[关键字.arg] = 关键字.value.value # type: ignore
|
|
251
|
+
elif isinstance(关键字.value, ast.NameConstant):
|
|
252
|
+
参数字典[关键字.arg] = 关键字.value.value # type: ignore
|
|
253
|
+
elif isinstance(关键字.value, ast.Name):
|
|
254
|
+
参数字典[关键字.arg] = 关键字.value.id # type: ignore
|
|
255
|
+
elif isinstance(关键字.value, ast.Str):
|
|
256
|
+
参数字典[关键字.arg] = 关键字.value.s # type: ignore
|
|
257
|
+
elif isinstance(关键字.value, ast.Num):
|
|
258
|
+
参数字典[关键字.arg] = 关键字.value.n # type: ignore
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class AOT编译器:
|
|
262
|
+
"""
|
|
263
|
+
AOT (Ahead-Of-Time) 编译器
|
|
264
|
+
|
|
265
|
+
批量预编译多个 JIT 函数,消除首次调用延迟
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
def __init__(
|
|
269
|
+
self,
|
|
270
|
+
优化级别: str = "O2",
|
|
271
|
+
启用并行编译: bool = True,
|
|
272
|
+
最大进程数: Optional[int] = None,
|
|
273
|
+
启用LTO: bool = False,
|
|
274
|
+
启用向量化: bool = False,
|
|
275
|
+
SIMD指令集: Optional[str] = None
|
|
276
|
+
):
|
|
277
|
+
"""
|
|
278
|
+
初始化 AOT 编译器
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
优化级别: 编译优化级别(O0/O1/O2/O3/Os/Ofast/Og/Oz)
|
|
282
|
+
启用并行编译: 是否启用并行编译
|
|
283
|
+
最大进程数: 最大并行进程数
|
|
284
|
+
启用LTO: 是否启用链接时优化(LTO)
|
|
285
|
+
启用向量化: 是否启用 SIMD 向量化优化
|
|
286
|
+
SIMD指令集: 指定 SIMD 指令集(None 表示自动检测)
|
|
287
|
+
"""
|
|
288
|
+
self.优化级别 = 优化级别
|
|
289
|
+
self.启用并行编译 = 启用并行编译
|
|
290
|
+
self.最大进程数 = 最大进程数
|
|
291
|
+
self.启用LTO = 启用LTO
|
|
292
|
+
self.启用向量化 = 启用向量化
|
|
293
|
+
self.SIMD指令集 = SIMD指令集
|
|
294
|
+
self.编译结果: List[Dict[str, Any]] = []
|
|
295
|
+
|
|
296
|
+
def 编译函数列表(
|
|
297
|
+
self,
|
|
298
|
+
函数列表: List[JIT函数信息],
|
|
299
|
+
显示进度: bool = True
|
|
300
|
+
) -> Dict[str, Any]:
|
|
301
|
+
"""
|
|
302
|
+
批量编译 JIT 函数列表
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
函数列表: JIT 函数信息列表
|
|
306
|
+
显示进度: 是否显示编译进度
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
编译统计信息
|
|
310
|
+
"""
|
|
311
|
+
总数 = len(函数列表)
|
|
312
|
+
成功数 = 0
|
|
313
|
+
失败数 = 0
|
|
314
|
+
跳过数 = 0 # 缓存命中
|
|
315
|
+
结果列表 = []
|
|
316
|
+
|
|
317
|
+
日志.信息(f"开始 AOT 编译 {总数} 个函数...")
|
|
318
|
+
|
|
319
|
+
for 索引, 信息 in enumerate(函数列表, 1):
|
|
320
|
+
if 显示进度:
|
|
321
|
+
日志.信息(f"[{索引}/{总数}] 编译 {信息.模块名}.{信息.函数名}...")
|
|
322
|
+
|
|
323
|
+
结果 = self._编译单个函数(信息)
|
|
324
|
+
结果列表.append(结果)
|
|
325
|
+
|
|
326
|
+
if 结果["状态"] == "成功":
|
|
327
|
+
成功数 += 1
|
|
328
|
+
if 显示进度 and 结果.get("使用缓存"):
|
|
329
|
+
日志.信息(f" ✓ 缓存命中")
|
|
330
|
+
elif 显示进度:
|
|
331
|
+
日志.信息(f" ✓ 编译成功")
|
|
332
|
+
elif 结果["状态"] == "跳过":
|
|
333
|
+
跳过数 += 1
|
|
334
|
+
if 显示进度:
|
|
335
|
+
日志.信息(f" - 跳过({结果.get('原因', '未知原因')})")
|
|
336
|
+
else: # 失败
|
|
337
|
+
失败数 += 1
|
|
338
|
+
日志.错误(f" ✗ 编译失败: {结果.get('错误', '未知错误')}")
|
|
339
|
+
|
|
340
|
+
统计 = {
|
|
341
|
+
"总数": 总数,
|
|
342
|
+
"成功": 成功数,
|
|
343
|
+
"失败": 失败数,
|
|
344
|
+
"跳过": 跳过数,
|
|
345
|
+
"结果列表": 结果列表
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
日志.信息(
|
|
349
|
+
f"AOT 编译完成: {成功数} 成功, {失败数} 失败, {跳过数} 跳过"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
return 统计
|
|
353
|
+
|
|
354
|
+
def _编译单个函数(self, 信息: JIT函数信息) -> Dict[str, Any]:
|
|
355
|
+
"""
|
|
356
|
+
编译单个 JIT 函数
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
编译结果字典
|
|
360
|
+
"""
|
|
361
|
+
if 信息.函数对象 is None:
|
|
362
|
+
return {
|
|
363
|
+
"函数名": 信息.函数名,
|
|
364
|
+
"模块名": 信息.模块名,
|
|
365
|
+
"状态": "跳过",
|
|
366
|
+
"原因": "无法导入函数对象"
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
# 如果传入的是已装饰的转译器对象,提取原始函数
|
|
370
|
+
原始函数 = 信息.函数对象
|
|
371
|
+
if isinstance(原始函数, Py转Cpp转译器):
|
|
372
|
+
原始函数 = 原始函数.目标函数
|
|
373
|
+
|
|
374
|
+
try:
|
|
375
|
+
# 创建编译器实例,传入 LTO 和向量化设置
|
|
376
|
+
编译器实例 = Cpp编译器(
|
|
377
|
+
优化级别=self.优化级别,
|
|
378
|
+
启用LTO=self.启用LTO,
|
|
379
|
+
启用向量化=self.启用向量化,
|
|
380
|
+
SIMD指令集=self.SIMD指令集
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# 创建转译器实例
|
|
384
|
+
转译器实例 = Py转Cpp转译器(
|
|
385
|
+
原始函数,
|
|
386
|
+
编译器实例,
|
|
387
|
+
可执行文件名=信息.装饰器参数.get("可执行文件名"),
|
|
388
|
+
总是重编=False, # AOT 不强制重编,利用缓存
|
|
389
|
+
启用并行编译=self.启用并行编译,
|
|
390
|
+
最大进程数=self.最大进程数
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# 检查是否已有缓存
|
|
394
|
+
库文件名 = 转译器实例.获取库文件名()
|
|
395
|
+
库路径 = os.path.join(全局上下文.工作目录, 库文件名)
|
|
396
|
+
使用缓存 = os.path.exists(库路径)
|
|
397
|
+
|
|
398
|
+
if not 使用缓存:
|
|
399
|
+
# 执行编译
|
|
400
|
+
转译器实例.编译()
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
"函数名": 信息.函数名,
|
|
404
|
+
"模块名": 信息.模块名,
|
|
405
|
+
"源文件": 信息.源文件路径,
|
|
406
|
+
"行号": 信息.行号,
|
|
407
|
+
"状态": "成功",
|
|
408
|
+
"使用缓存": 使用缓存,
|
|
409
|
+
"库文件": 库文件名
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
except Exception as e:
|
|
413
|
+
return {
|
|
414
|
+
"函数名": 信息.函数名,
|
|
415
|
+
"模块名": 信息.模块名,
|
|
416
|
+
"状态": "失败",
|
|
417
|
+
"错误": str(e)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def 编译包(
|
|
422
|
+
根目录: str,
|
|
423
|
+
递归: bool = True,
|
|
424
|
+
排除目录: Optional[Set[str]] = None,
|
|
425
|
+
包含测试: bool = False,
|
|
426
|
+
优化级别: str = "O2",
|
|
427
|
+
启用并行编译: bool = True,
|
|
428
|
+
最大进程数: Optional[int] = None,
|
|
429
|
+
启用LTO: bool = False,
|
|
430
|
+
启用向量化: bool = False,
|
|
431
|
+
SIMD指令集: Optional[str] = None,
|
|
432
|
+
显示进度: bool = True
|
|
433
|
+
) -> Dict[str, Any]:
|
|
434
|
+
"""
|
|
435
|
+
扫描并编译包中的所有 JIT 函数
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
根目录: 包的根目录
|
|
439
|
+
递归: 是否递归扫描子目录
|
|
440
|
+
排除目录: 要排除的目录名集合
|
|
441
|
+
包含测试: 是否包含测试文件
|
|
442
|
+
优化级别: 编译优化级别
|
|
443
|
+
启用并行编译: 是否启用并行编译
|
|
444
|
+
最大进程数: 最大并行进程数
|
|
445
|
+
启用LTO: 是否启用链接时优化(LTO)
|
|
446
|
+
启用向量化: 是否启用 SIMD 向量化优化
|
|
447
|
+
SIMD指令集: 指定 SIMD 指令集(None 表示自动检测)
|
|
448
|
+
显示进度: 是否显示编译进度
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
编译统计信息
|
|
452
|
+
"""
|
|
453
|
+
扫描器 = 包扫描器(根目录)
|
|
454
|
+
jit函数列表 = 扫描器.扫描目录(递归, 排除目录, 包含测试)
|
|
455
|
+
|
|
456
|
+
if not jit函数列表:
|
|
457
|
+
日志.警告("未找到任何 JIT 函数")
|
|
458
|
+
return {"总数": 0, "成功": 0, "失败": 0, "跳过": 0, "结果列表": []}
|
|
459
|
+
|
|
460
|
+
编译器 = AOT编译器(优化级别, 启用并行编译, 最大进程数, 启用LTO, 启用向量化, SIMD指令集)
|
|
461
|
+
return 编译器.编译函数列表(jit函数列表, 显示进度)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def 编译函数列表(
|
|
465
|
+
函数列表: List[Callable],
|
|
466
|
+
优化级别: str = "O2",
|
|
467
|
+
启用并行编译: bool = True,
|
|
468
|
+
最大进程数: Optional[int] = None,
|
|
469
|
+
启用LTO: bool = False,
|
|
470
|
+
启用向量化: bool = False,
|
|
471
|
+
SIMD指令集: Optional[str] = None,
|
|
472
|
+
显示进度: bool = True
|
|
473
|
+
) -> Dict[str, Any]:
|
|
474
|
+
"""
|
|
475
|
+
编译给定的函数列表
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
函数列表: 要编译的函数列表(可以是普通函数或 JIT 装饰后的转译器对象)
|
|
479
|
+
优化级别: 编译优化级别
|
|
480
|
+
启用并行编译: 是否启用并行编译
|
|
481
|
+
最大进程数: 最大并行进程数
|
|
482
|
+
启用LTO: 是否启用链接时优化(LTO)
|
|
483
|
+
启用向量化: 是否启用 SIMD 向量化优化
|
|
484
|
+
SIMD指令集: 指定 SIMD 指令集(None 表示自动检测)
|
|
485
|
+
显示进度: 是否显示编译进度
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
编译统计信息
|
|
489
|
+
"""
|
|
490
|
+
jit函数信息列表 = []
|
|
491
|
+
|
|
492
|
+
for 函数 in 函数列表:
|
|
493
|
+
# 处理 Py转Cpp转译器 对象(已通过 @jit 装饰的函数)
|
|
494
|
+
if isinstance(函数, Py转Cpp转译器):
|
|
495
|
+
try:
|
|
496
|
+
# 从转译器中获取原始函数信息
|
|
497
|
+
原始函数 = 函数.目标函数
|
|
498
|
+
信息 = JIT函数信息(
|
|
499
|
+
函数名=函数.函数名,
|
|
500
|
+
函数对象=原始函数, # 使用原始函数对象进行编译
|
|
501
|
+
模块名=原始函数.__module__,
|
|
502
|
+
源文件路径=inspect.getfile(原始函数),
|
|
503
|
+
行号=inspect.getsourcelines(原始函数)[1]
|
|
504
|
+
)
|
|
505
|
+
jit函数信息列表.append(信息)
|
|
506
|
+
except Exception as e:
|
|
507
|
+
日志.警告(f"无法获取转译器 {函数.函数名} 的信息: {e}")
|
|
508
|
+
continue
|
|
509
|
+
|
|
510
|
+
# 处理普通函数(未通过 @jit 装饰)
|
|
511
|
+
if not callable(函数):
|
|
512
|
+
continue
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
信息 = JIT函数信息(
|
|
516
|
+
函数名=函数.__name__,
|
|
517
|
+
函数对象=函数,
|
|
518
|
+
模块名=函数.__module__,
|
|
519
|
+
源文件路径=inspect.getfile(函数),
|
|
520
|
+
行号=inspect.getsourcelines(函数)[1]
|
|
521
|
+
)
|
|
522
|
+
jit函数信息列表.append(信息)
|
|
523
|
+
except Exception as e:
|
|
524
|
+
日志.警告(f"无法获取函数 {函数} 的信息: {e}")
|
|
525
|
+
|
|
526
|
+
编译器 = AOT编译器(优化级别, 启用并行编译, 最大进程数, 启用LTO, 启用向量化, SIMD指令集)
|
|
527
|
+
return 编译器.编译函数列表(jit函数信息列表, 显示进度)
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def main():
|
|
531
|
+
"""CLI 入口点"""
|
|
532
|
+
parser = argparse.ArgumentParser(
|
|
533
|
+
description="l0n0lc AOT 预编译工具 - 批量预编译 JIT 函数",
|
|
534
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
535
|
+
epilog="""
|
|
536
|
+
示例:
|
|
537
|
+
# 编译当前目录下的所有 JIT 函数
|
|
538
|
+
l0n0lc-aot-compile
|
|
539
|
+
|
|
540
|
+
# 编译指定目录
|
|
541
|
+
l0n0lc-aot-compile --path /path/to/project
|
|
542
|
+
|
|
543
|
+
# 使用 O3 优化级别
|
|
544
|
+
l0n0lc-aot-compile --optimization O3
|
|
545
|
+
|
|
546
|
+
# 启用链接时优化(LTO)
|
|
547
|
+
l0n0lc-aot-compile --enable-lto
|
|
548
|
+
|
|
549
|
+
# 启用 SIMD 向量化优化
|
|
550
|
+
l0n0lc-aot-compile --enable-vectorize
|
|
551
|
+
|
|
552
|
+
# 指定 SIMD 指令集
|
|
553
|
+
l0n0lc-aot-compile --enable-vectorize --simd AVX2
|
|
554
|
+
|
|
555
|
+
# 包含测试文件
|
|
556
|
+
l0n0lc-aot-compile --include-tests
|
|
557
|
+
|
|
558
|
+
# 禁用并行编译
|
|
559
|
+
l0n0lc-aot-compile --no-parallel
|
|
560
|
+
|
|
561
|
+
# 组合多个优化选项
|
|
562
|
+
l0n0lc-aot-compile --optimization O3 --enable-lto --enable-vectorize
|
|
563
|
+
"""
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
parser.add_argument(
|
|
567
|
+
"--path", "-p",
|
|
568
|
+
default=".",
|
|
569
|
+
help="要扫描的目录路径(默认为当前目录)"
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
parser.add_argument(
|
|
573
|
+
"--optimization", "-O",
|
|
574
|
+
choices=["O0", "O1", "O2", "O3", "Os", "Ofast", "Og", "Oz"],
|
|
575
|
+
default="O2",
|
|
576
|
+
help="编译优化级别(默认为 O2)"
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
parser.add_argument(
|
|
580
|
+
"--no-parallel",
|
|
581
|
+
action="store_true",
|
|
582
|
+
help="禁用并行编译"
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
parser.add_argument(
|
|
586
|
+
"--max-processes",
|
|
587
|
+
type=int,
|
|
588
|
+
default=None,
|
|
589
|
+
help="最大并行进程数(默认为 CPU 核心数)"
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
parser.add_argument(
|
|
593
|
+
"--include-tests",
|
|
594
|
+
action="store_true",
|
|
595
|
+
help="包含测试文件(test_*.py)"
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
parser.add_argument(
|
|
599
|
+
"--exclude",
|
|
600
|
+
action="append",
|
|
601
|
+
help="要排除的目录名(可多次使用)"
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
parser.add_argument(
|
|
605
|
+
"--enable-lto",
|
|
606
|
+
action="store_true",
|
|
607
|
+
help="启用链接时优化(LTO),可提升运行时性能 10-30%%,但会增加编译时间"
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
parser.add_argument(
|
|
611
|
+
"--enable-vectorize",
|
|
612
|
+
action="store_true",
|
|
613
|
+
help="启用 SIMD 向量化优化,可提升数组操作性能 2-8x"
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
parser.add_argument(
|
|
617
|
+
"--simd",
|
|
618
|
+
choices=["SSE2", "SSE4_2", "AVX", "AVX2", "AVX512F", "NEON"],
|
|
619
|
+
default=None,
|
|
620
|
+
help="指定 SIMD 指令集(默认为自动检测)"
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
parser.add_argument(
|
|
624
|
+
"--quiet", "-q",
|
|
625
|
+
action="store_true",
|
|
626
|
+
help="静默模式,减少输出"
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
parser.add_argument(
|
|
630
|
+
"--version", "-v",
|
|
631
|
+
action="version",
|
|
632
|
+
version="l0n0lc AOT 编译器 v0.3.0"
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
args = parser.parse_args()
|
|
636
|
+
|
|
637
|
+
# 设置日志级别
|
|
638
|
+
if args.quiet:
|
|
639
|
+
日志.设置级别(日志级别.警告)
|
|
640
|
+
|
|
641
|
+
# 构建排除目录集合
|
|
642
|
+
默认排除目录 = {
|
|
643
|
+
"__pycache__",
|
|
644
|
+
".git",
|
|
645
|
+
".venv",
|
|
646
|
+
"venv",
|
|
647
|
+
"env",
|
|
648
|
+
"build",
|
|
649
|
+
"dist",
|
|
650
|
+
".eggs",
|
|
651
|
+
"*.egg-info",
|
|
652
|
+
}
|
|
653
|
+
if args.exclude:
|
|
654
|
+
默认排除目录.update(args.exclude)
|
|
655
|
+
|
|
656
|
+
# 执行编译
|
|
657
|
+
统计 = 编译包(
|
|
658
|
+
根目录=args.path,
|
|
659
|
+
递归=True,
|
|
660
|
+
排除目录=默认排除目录,
|
|
661
|
+
包含测试=args.include_tests,
|
|
662
|
+
优化级别=args.optimization,
|
|
663
|
+
启用并行编译=not args.no_parallel,
|
|
664
|
+
最大进程数=args.max_processes,
|
|
665
|
+
启用LTO=args.enable_lto,
|
|
666
|
+
启用向量化=args.enable_vectorize,
|
|
667
|
+
SIMD指令集=args.simd,
|
|
668
|
+
显示进度=not args.quiet
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
# 返回适当的退出码
|
|
672
|
+
if 统计["失败"] > 0:
|
|
673
|
+
sys.exit(1)
|
|
674
|
+
else:
|
|
675
|
+
sys.exit(0)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
if __name__ == "__main__":
|
|
679
|
+
main()
|