- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
将纯 Python 空操作函数与用 @numba.jit
修饰的空操作函数进行比较,即:
import numba
@numba.njit
def boring_numba():
pass
def call_numba(x):
for t in range(x):
boring_numba()
def boring_normal():
pass
def call_normal(x):
for t in range(x):
boring_normal()
如果我们用 %timeit
计时,我们会得到以下结果:
%timeit call_numba(int(1e7))
792 ms ± 5.51 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit call_normal(int(1e7))
737 ms ± 2.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
一切都非常合理; numba 函数的开销很小,但并不多。
但是,如果我们使用 cProfile
分析这段代码,我们会得到以下信息:
cProfile.run('call_numba(int(1e7)); call_normal(int(1e7))', sort='cumulative')
ncalls tottime percall cumtime percall filename:lineno(function)
76/1 0.003 0.000 8.670 8.670 {built-in method builtins.exec}
1 6.613 6.613 7.127 7.127 experiments.py:10(call_numba)
1 1.111 1.111 1.543 1.543 experiments.py:17(call_normal)
10000000 0.432 0.000 0.432 0.000 experiments.py:14(boring_normal)
10000000 0.428 0.000 0.428 0.000 experiments.py:6(boring_numba)
1 0.000 0.000 0.086 0.086 dispatcher.py:72(compile)
cProfile
认为调用 numba 函数的开销很大。这扩展到“真实”代码:我有一个简单地调用我的昂贵计算的函数(计算是 numba-JIT 编译的),cProfile
报告说包装函数占用了大约三分之一的时间总时间。
我不介意 cProfile
增加一些开销,但如果它在增加开销的位置上存在大量不一致,那它就不是很有帮助了。有谁知道为什么会发生这种情况,是否有什么办法可以解决,和/或是否有任何其他分析工具不会与 numba 交互不良?
最佳答案
当您创建一个 numba 函数时,您实际上创建了一个 numba Dispatcher
对象。该对象将对 boring_numba
的“调用”“重定向”到正确的(就类型而言)内部“jitted”函数。因此,即使您创建了一个名为 boring_numba
的函数 - 该函数并未被调用,但被调用的是一个基于您的函数的编译函数。
这样你就可以看到函数 boring_numba
在分析 的过程中被调用(即使它不是,调用的是
对象需要 Hook 到当前线程状态并检查是否有分析器/跟踪器正在运行,如果"is",它看起来像是调用了 CPUDispatcher.__call__
) Dispatcherboring_numba
。最后一步是导致开销是因为它必须为 boring_numba
伪造一个“Python 堆栈框架”。
更具技术性:
当您调用 numba 函数 boring_numba
时,它实际上调用了 Dispatcher_Call
这是 call_cfunc
的包装这是主要的区别:当你有一个运行分析器的代码时,处理分析器的代码构成了函数调用的大部分(只需比较 if (tstate->use_tracing && tstate->c_profilefunc)
如果没有分析器/跟踪器,则使用正在运行的 else
分支进行分支):
static PyObject *
call_cfunc(DispatcherObject *self, PyObject *cfunc, PyObject *args, PyObject *kws, PyObject *locals)
{
PyCFunctionWithKeywords fn;
PyThreadState *tstate;
assert(PyCFunction_Check(cfunc));
assert(PyCFunction_GET_FLAGS(cfunc) == METH_VARARGS | METH_KEYWORDS);
fn = (PyCFunctionWithKeywords) PyCFunction_GET_FUNCTION(cfunc);
tstate = PyThreadState_GET();
if (tstate->use_tracing && tstate->c_profilefunc)
{
/*
* The following code requires some explaining:
*
* We want the jit-compiled function to be visible to the profiler, so we
* need to synthesize a frame for it.
* The PyFrame_New() constructor doesn't do anything with the 'locals' value if the 'code's
* 'CO_NEWLOCALS' flag is set (which is always the case nowadays).
* So, to get local variables into the frame, we have to manually set the 'f_locals'
* member, then call `PyFrame_LocalsToFast`, where a subsequent call to the `frame.f_locals`
* property (by virtue of the `frame_getlocals` function in frameobject.c) will find them.
*/
PyCodeObject *code = (PyCodeObject*)PyObject_GetAttrString((PyObject*)self, "__code__");
PyObject *globals = PyDict_New();
PyObject *builtins = PyEval_GetBuiltins();
PyFrameObject *frame = NULL;
PyObject *result = NULL;
if (!code) {
PyErr_Format(PyExc_RuntimeError, "No __code__ attribute found.");
goto error;
}
/* Populate builtins, which is required by some JITted functions */
if (PyDict_SetItemString(globals, "__builtins__", builtins)) {
goto error;
}
frame = PyFrame_New(tstate, code, globals, NULL);
if (frame == NULL) {
goto error;
}
/* Populate the 'fast locals' in `frame` */
Py_XDECREF(frame->f_locals);
frame->f_locals = locals;
Py_XINCREF(frame->f_locals);
PyFrame_LocalsToFast(frame, 0);
tstate->frame = frame;
C_TRACE(result, fn(PyCFunction_GET_SELF(cfunc), args, kws));
tstate->frame = frame->f_back;
error:
Py_XDECREF(frame);
Py_XDECREF(globals);
Py_XDECREF(code);
return result;
}
else
return fn(PyCFunction_GET_SELF(cfunc), args, kws);
}
我假设这个额外的代码(如果分析器正在运行)会在您进行 cProfile 时减慢函数的速度。
有点不幸的是,当您运行分析器时,numba 函数会增加如此多的开销,但如果您在 numba 函数中做任何实质性的事情,那么减速实际上几乎可以忽略不计。如果您还要在 numba 函数中移动 for
循环,那就更是如此。
如果您注意到 numba 函数(运行或不运行探查器)花费太多时间,那么您可能调用它的频率太高了。然后你应该检查你是否真的可以将循环移动到 numba 函数内或将包含循环的代码包装在另一个 numba 函数中。
注意:所有这些都是(有点)推测,我实际上并没有使用调试符号构建 numba 并分析 C 代码以防分析器正在运行。然而,如果有一个分析器正在运行,那么操作的数量使得这看起来非常合理。所有这些都假设 numba 0.39,不确定这是否也适用于过去的版本。
关于python - cProfile 在调用 numba jit 函数时会增加大量开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51384157/
如何从一个函数中调用 cProfile,使用它来调用和分析另一个函数? 我有一个函数 start(),它是从我的网页调用的(使用 Django)。在此函数中,我放置了 cProfile 调用: cPr
我正在尝试在我的 python 脚本上运行 cProfile,我关心的是运行所需的总时间。有没有办法修改 python -m cProfile myscript.py 所以输出只是总时间? 最佳答案
对于初学者的问题很抱歉,但我无法弄清楚 cProfile(我真的是 Python 的新手) 我可以通过我的终端运行它: python -m cProfile myscript.py 但我需要在网络服务
我目前正在学习如何使用 cProfile,我有一些疑问。 我目前正在尝试分析以下脚本: import time def fast(): print("Fast!") def slow():
基本上,当我运行 cProfile 模块时,它会跳过一些函数,而普通的配置文件模块会产生此错误。 The debugged program raised the exception unhan
我附上了 Python 脚本的 cProfile 结果的屏幕截图。我知道第二行是指 arcpy 站点包中的地理处理函数。但是,我不清楚第一行指的是什么: C:\Program Files (x86)\
我想知道为什么我的基于 pyzmq 和 protobuf 的消息传递 ping-pong 比预期的要慢得多,所以我使用 cProfile 来检查您在本文末尾找到的脚本。 protoc --python
我正在尝试使用 cProfile 来分析一些 python 代码。我相信我需要使用 cProfile.runcall(),而不是 cProfile.run(),因为我要运行的方法是 self.func
我正在尝试用 python 分析我的项目,但内存不足。 我的项目本身相当占用内存,但在 cProfile 下运行时,即使是半大小的运行也会因“MemoryError”而终止。 进行较小的运行并不是一个
我正在尝试使用 cProfile.run 分析嵌套函数。我知道 cProfile 可能与我调用它的范围不在同一范围内运行,但我不太确定实现这一目标的惯用方法是什么。这是一个 MVCE: def foo
我有一个带有 @classmethod 的基类,它充当许多后代类中大量方法的装饰器。 class BaseClass(): @classmethod def some_decorato
我在一些代码上运行了 cprofile,除其他外,它产生了几个线程来完成大部分工作。当我查看分析的输出时,我没有看到线程内调用的所有函数的日志记录。我确定他们被调用了,因为他们做的事情很容易看到,例如
我开始使用 cProfile 来分析我的 python 脚本。我注意到一些非常奇怪的事情。 当我使用 time 来测量我的脚本的运行时间时,它需要 4.3 秒。 当我使用 python -m cPro
我试图使用 cProfile 对我的代码进行性能测试,但遗憾的是无论我如何尝试,cProfile 都无法正常运行。这是我所做的: import cProfile cProfile.run('addNu
我在 mymodule 中有这些文件 mymodule ├── config.py ├── __init__.py └── lib.py 有了这个简单的内容: # config.py NAME = "
我在一个名为 bot4CA.py 的模块上使用 cProfile,所以在控制台中我输入: python -m cProfile -o thing.txt bot4CA.py 模块运行并退出后,它会创建
我正在使用 cProfile 分析一个 Python 应用程序,我发现它的输出非常冗长。我正在使用此代码创建配置文件并将其可视化: PYTHONPATH=. \ python3 \ -
cProfile 在输出中显示了很多内置函数调用。我们可以将输出限制为我编写的代码吗?因此,在下面的示例中,我能否仅看到来自 testrun 的行或来自驻留在同一脚本中的 testrun() 调用的函
我从我的 cProfile 输出中获得了大约 300 个条目,每次使用它时都必须向上滚动很长时间。 有没有办法让 cProfile 只打印前 10 行之类的东西? 最佳答案 您可以按“累积”排序并使用
另一方面,timeit 运行代码 1,000,000 次,以获得与其他代码的合理渐近比较。 cProfile 仅运行代码一次,结果中只有 3 个小数位 (0.000),不足以了解完整情况。 你会得到如
我是一名优秀的程序员,十分优秀!