【问题标题】:Performance impact of Python classes: Scientific ComputingPython 类的性能影响:科学计算
【发布时间】:2017-09-23 04:23:15
【问题描述】:

所以我正在编写粒子模型。该模拟将涉及创建许多具有物种索引(以识别粒子类型)和 3D 空间中的位置/速度等属性的粒子。它还将具有基于其在空间中的位置的属性,这些属性将在模拟运行期间计算。我之前一直在使用一个巨大的 numpy 数组运行这个模型,其中每一行对应一个粒子,每一列对应一个粒子属性。

我的主要问题是:如果我要创建一个“粒子”类以便生成代表每个粒子的实例,那么在计算速度方面是否会影响性能?如果我想运行一个包含一百万个粒子的程序,将所有数字放在一个大数组中是否更有效(即使它不太可读),还是使用此类的实例并修改模拟进行时的实例属性?或者是这些类很好,但是在 Python 列表/NumPy 数组中存储这么多类实例真的会减慢执行速度?

顺便说一句,这个程序将与 Python 模块“Numba”一起使用,该模块对数值计算进行了大量优化(特别是通过诸如 jitclass http://numba.pydata.org/numba-doc/latest/user/jitclass.html#numba.jitclass 之类的东西)。但是,我不想通过在我的模拟中声明导致其运行效率低下的变量来破坏该模块的性能优势。

谢谢!

【问题讨论】:

  • 如果你可以将类优化为一个结构,那么真正使用 jitclasses 就不会有性能损失。也就是说,我所在领域的大多数工作传统上都是编写 C++ 扩展,然后使用 SWIG 或 Boost::Python 之类的包装器将它们包装在 Python 中。最终,编写简单的测试用例和配置文件、配置文件、配置文件。
  • 我不知道 numba 能做什么,但通常对象 dtype 数组比数字数组慢得多。像列表一样,这些数组值实际上是指向内存中其他地方的对象的指针。在stackoverflow.com/questions/46350208/… 中查看我最近的回答。
  • 创建 Particles 类的动机是什么——更好的抽象?如果是这样,您可能会创建一个表示粒子群的类,其中包含获取/修改粒子信息的方法。这将使您能够创建更高级别的抽象来处理粒子,而且还将底层粒子数据存储在一个数组中,因此您不会将数据分散到内存中。
  • 如果您真的关心性能,最好花时间用 C 或 C++ 等编译语言实现代码。 Cython 可能是一个很好的中间步骤。但最终,基准测试是为您的应用程序获得“正确”答案的唯一方法。试着做一些能代表你实际问题的玩具问题,看看这两种方法的比较。

标签: python performance numpy numba


【解决方案1】:

我之前一直在使用一个巨大的 numpy 数组运行这个模型

听起来是个好主意。

如果我要创建一个“粒子”类,以便生成代表每个粒子的实例,在计算速度方面是否会影响性能?

它可能会慢得多。

您现有的 NumPy 数组解决方案可让您在一个大数组中表示所有粒子:

id species x y z dx dy dz
id species x y z dx dy dz
id species x y z dx dy dz

如果您将其更改为对每个粒子使用一个类实例,您仍然可以将它们保存在 NumPy 数组(或列表)中,但它会如下所示:

object -> [id species x y z dx dy dz]
object -> [id species x y z dx dy dz]
object -> [id species x y z dx dy dz]

你需要分配四个 N+1 个对象,而不是 1 个(数组)。

我会坚持原来的设计,即巨大的 NumPy 数组,除非它会造成重大问题。

【讨论】:

  • 老实说,这可能是最好的答案,而不是使用 Cython/Swig/Boost 深入研究 C++。 Jitclasses 可能更快,但我不相信它们在 NumPy 中被视为对象,这意味着指针和非连续存储,因此访问速度慢和额外开销。 C++ 扩展可以解决很多这样的问题,而且实现起来也很简单,但是对于这样一个简单的例子来说是不值得的(更不用说打包问题了)。
【解决方案2】:

您应该在这里使用结构化数据类型:

particle_dtype = np.dtype([
    ('id', int),
    ('species', (np.unicode_, 16)),
    ('pos', np.float32, 3),
    ('vel', np.float32, 3)
])

particles = np.empty(100, dtype=particle_dtype)
particles[0]['id'] = 1
particles['pos'] += particles['vel'] * dt

【讨论】:

    猜你喜欢
    • 2011-02-14
    • 1970-01-01
    • 2017-11-05
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 2017-07-07
    • 1970-01-01
    相关资源
    最近更新 更多