如果你到达这里,你想潜入,或使用,更先进的工具。对于第一次贡献者和大多数日常开发来说,这通常不是必需的。它们很少被使用,例如在一个新的NumPy发行版附近,或者在一个大的或者特别复杂的变更发生时。
由于并非所有这些工具都是在常规基础上使用的,并且仅在某些系统上可用,请期待差异、问题或怪癖;如果您遇到困难,我们将很乐意为您提供帮助,并感谢您对这些工作流的任何改进或建议。
大多数开发只需要一个典型的调试工具链,如中所示 Debugging . 但举例来说,内存泄漏可能特别微妙或难以缩小。
我们并不期望这些工具中的任何一个能被大多数贡献者运行。但是,您可以确保我们可以更轻松地追踪这些问题:
测试应该覆盖所有代码路径,包括错误路径。
试着写简短的测试。如果您有一个非常复杂的测试,也可以考虑创建一个更简单的测试。这可能很有帮助,因为通常只容易找到哪个测试触发了问题,而不容易找到测试的哪一行。
从不使用 np.empty 如果数据被读取/使用。 valgrind 将注意到这一点并报告错误。当你不在乎随机值的时候,你可以不在乎。
np.empty
valgrind
这将有助于我们在您的更改被发布之前抓住任何疏忽,意味着您不必担心出现引用计数错误,这可能会令人生畏。
Python的调试版本很容易获得,例如 debian 系统,并且可以在所有平台上使用。运行测试或终端通常很容易:
debian
python3.8d runtests.py # or python3.8d runtests.py --ipython
已经在书中提到了 Debugging .
Python调试版本将有助于:
找出可能导致随机行为的错误。一个例子是当一个对象被删除后仍然被使用。
Python调试构建允许检查正确的引用计数。此操作使用以下附加命令:
sys.gettotalrefcount() sys.getallocatedblocks()
pytest
仅使用调试python构建运行测试套件本身不会发现很多错误。Python调试构建的另一个优点是它允许检测内存泄漏。
使这更容易的一个工具是 pytest-leaks ,可以使用 pip . 不幸的是, pytest 本身可能会泄漏内存,但通常(当前)可以通过删除以下内容来获得良好的效果:
pip
@pytest.fixture(autouse=True) def add_np(doctest_namespace): doctest_namespace['np'] = numpy @pytest.fixture(autouse=True) def env_setup(monkeypatch): monkeypatch.setenv('PYTHONHASHSEED', '0')
从 numpy/conftest.py (这可能会随着新的 pytest-leaks 版本或 pytest 更新)。
numpy/conftest.py
pytest-leaks
这样可以方便地运行测试套件或其一部分:
python3.8d runtests.py -t numpy/core/tests/test_multiarray.py -- -R2:3 -s
在哪里? -R2:3 是 pytest-leaks 命令(请参阅其文档) -s 导致输出打印,可能是必要的(在某些版本中,捕获的输出被检测为泄漏)。
-R2:3
-s
请注意,有些测试已知(甚至设计)会泄漏引用,我们尝试对它们进行标记,但会出现一些误报。
Valgrind是发现某些内存访问问题的强大工具,应该在复杂的C代码上运行。基本用途 valgrind 通常不需要超过:
PYTHONMALLOC=malloc python runtests.py
在哪里? PYTHONMALLOC=malloc 是避免python本身误报的必要条件。根据系统和valgrind版本,您可能会看到更多误报。 valgrind 支持“suppressions”来忽略其中的一些,并且Python有一个suppression文件(甚至还有一个编译时选项),如果您觉得有必要的话,它可能会有所帮助。
PYTHONMALLOC=malloc
Valgrind帮助:
查找未初始化变量/内存的使用。
检测内存访问冲突(在分配的内存之外读取或写入)。
发现 many 内存泄漏。注意,对于 most 泄漏python调试构建方法(和 pytest-leaks )更敏感。原因是 valgrind 只能检测内存是否丢失。如果:
dtype = np.dtype(np.int64) arr.astype(dtype=dtype)
有不正确的引用计数 dtype ,这是一个bug,但是valgrind看不到它,因为 np.dtype(np.int64) 总是返回相同的对象。但是,并不是所有的数据类型都是单例的,因此这可能会泄漏不同输入的内存。在极少数情况下,NumPy使用 malloc 而不是对Python调试构建不可见的Python内存分配器。 malloc 通常应避免,但也有一些例外(例如 PyArray_Dims 结构是公共API,不能使用Python分配器。)
dtype
np.dtype(np.int64)
malloc
PyArray_Dims
尽管使用valgrind进行内存泄漏检测比较慢,而且不太敏感,但它还是很方便的:您可以使用valgrind运行大多数程序,而无需修改。
注意事项:
Valgrind不支持numpy longdouble ,这意味着测试将失败或被标记为完全正确的错误。
longdouble
运行NumPy代码前后可能会出现一些错误。
缓存可能意味着错误(特别是内存泄漏)可能无法检测到,或者只能在以后不相关的时间检测到。
valgrind的一大优点是,除了valgrind本身之外,它没有任何需求(尽管您可能希望使用调试构建来实现更好的跟踪)。
您可以使用valgrind运行测试套件,当您只对几个测试感兴趣时,这就足够了:
PYTHOMMALLOC=malloc valgrind python runtests.py \ -t numpy/core/tests/test_multiarray.py -- --continue-on-collection-errors
注意 --continue-on-collection-errors ,由于缺少 longdouble 支持导致失败(如果不运行完整的测试套件,通常不需要这样做)。
--continue-on-collection-errors
如果您希望检测内存泄漏,您还需要 --show-leak-kinds=definite 可能还有更多的选择。就像对 pytest-leaks 已知某些测试会导致valgrind中的错误,可能会也可能不会被标记为错误。
--show-leak-kinds=definite
我们开发了 pytest-valgrind 其中:
分别报告每个测试的错误
将内存泄漏缩小到单个测试(默认情况下,valgrind只在程序停止后检查内存泄漏,这非常麻烦)。
请参考其 README 有关详细信息(它包括一个NumPy命令示例)。
README