基准¶
为了掌握Pymunk的实际性能,此页面包含了许多基准测试。
所有基准测试的完整代码可在 benchmarks 文件夹。
请注意,Pymunk6.0的基准测试还没有更新,但测试看起来很有希望。
获取和回调¶
为了在最常见的情况下衡量Pymunk造成的开销,我创建了两个微基准。它们应该显示实际包装代码的速度,这可以告诉Pymunk产生了多大的开销,以及不同的包装方法有多大的差异。
使用Pymunk的典型程序最常见的操作是从Pymunk对象中读出位置和角度。通常会在模拟中的每个对象的每一帧中执行此操作,因此这是决定操作速度的一个重要因素。
鉴于此,我们的第一个测试是:
t += b.position.x + b.position.y + b.angle
(见 pymunk-get.py )
运行它很简单,例如,对于Pymunk 4.0::
> python -m pip install pymunk==4.0
> python pymunk-get.py
我们所做的第二个测试是基于我们可以做的第二件重要事情,即使用回调,例如作为冲突处理程序或位置函数:
def f(b,dt):
b.position += (1,0)
s.step(0.01)
(见 pymunk-callback.py )
结果:¶
测试在搭载英特尔i7-4600U的惠普G1 1040笔记本电脑上运行。笔记本电脑运行Windows,测试在运行64位Debian的VirtualBox VM中运行。CPython测试使用来自Conda的CPython,而PyPy测试使用手动下载的PyPy。CPython2.7使用CFFI 1.7,其他测试使用CFFI 1.8。
请记住,这些结果并不能告诉您游戏/应用程序将如何运行,它们可以更多地被视为有助于识别性能问题和了解Python之间的区别。
Pymunk-Get:¶
CPython2.7.12 |
CPython3.5.2 |
PyPy 5.4.1 |
|
---|---|---|---|
Pymunk5.1 |
2.1s |
2.2s |
0.36s |
Pymunk5.0 |
4.3s |
4.5s |
0.37s |
Pymunk4.0 |
1.0s |
0.9s |
0.52s |
Pymunk-回调:¶
CPython2.7.12 |
CPython3.5.2 |
PyPy 5.4.1 |
|
---|---|---|---|
Pymunk5.1 |
5.7s |
6.8s |
1.1s |
Pymunk5.0 |
6.5s |
7.3s |
1.0s |
Pymunk4.0 |
5.1s |
6.5s |
4.5s |
从这些结果中我们可以看到,如果有可能,您应该使用PyPy,因为它比常规的CPython快得多。我们还可以看到,在Pymunk 4和5之间从Ctype迁移到CFFI在CPython中有负面影响,但对PyPy有积极影响,而Pymunk 5和PyPy一起使用是最快的选择。
速度在5.0到5.1之间的提升是因为Vec2d类及其在Pymunk中的内部处理方式被更改以提高性能。
批处理API¶
Pymunk 6.6.0引入了一个新的实验性批处理API,可以在批处理中高效地检索车身和碰撞数据,这些批处理经过优化,可以进一步高效地处理,例如使用NumPy。
为了测试这一点,有一个新的基准测试,它将获取位置和角度(Batch API的几乎每个用户都会使用的数据)正常地与新的API进行比较。该基准测试使用不同数量的主体运行,并将迭代次数调整为在合理时间内完成。
(见 pymunk-batch-api.py )
基准测试是使用内部预发布的Pymunk 6.6.0运行在Windows上,在ThinkPad X1 Carbon 7 Gen上使用CPython3.11和PyPy 3.10-v7.3.12。
结果:¶
下面我们可以看到,在一个空间中使用5个实体时,使用批处理API的速度已经更快了,如果使用CPython,它处理大量实体的速度大约是普通非批处理API的40倍。有了PyPy,改进要温和得多。
身体 |
正常API |
批处理API |
正常的PyPy |
批处理PYPy |
---|---|---|---|---|
1 |
2.2s |
4.2s |
0.4s |
0.5s |
5 |
2.0s |
0.8s |
0.3s |
0.3s |
10 |
2.1s |
0.4s |
0.3s |
0.2s |
100 |
2.2s |
0.09s |
0.3s |
0.2s |
1000 |
2.3s |
0.05s |
0.3s |
0.2s |
10000 |
不适用 |
0.04s |
不适用 |
0.2s |
50000 |
不适用 |
0.06s |
不适用 |
0.2s |
100000 |
不适用 |
0.07s |
不适用 |
0.2s |
得到的时间是获得位置和角度数据的时间除以物体数量的1000000倍。
由此我们可以看出,如果只有1个Body,那么使用普通API的速度是使用CPython的批处理API的两倍。然而,在5个主体中,批处理API的速度是普通API的(超过)两倍。这比预期的要好,也显示了潜力。
对于更多的主体,很明显,正常API的运行时或多或少是线性扩展的,这意味着无论主体的数量如何,获取单个主体的开销都是恒定的。对于批处理API,我们可以看到批处理有很高的开销,当我们达到大约1000个主体时,它开始像普通API一样扩展,每个主体的开销或多或少是恒定的。我们还可以看到,每个身体的时间略有增加,可能是因为收集结果所需的更大的阵列,或者是Chipmunk内部的一些其他开销。
另一方面,对于PyPy来说,结果就不那么令人兴奋了。使用普通API的PyPy已经非常快了,如 Pymunk-Get 基准测试,并且使用批处理API只提供了适度的改进。
与其他物理类库相比¶
备注
到目前为止(2023年),西蒙克(也是pybox2d)似乎没有得到维护。
西蒙克¶
Cymunk 是Chipmunk的另一种包装。与Pymunk不同,它使用Cython进行包装(Pymunk使用CFFI),这为它提供了不同的性能配置文件。然而,由于两者都是围绕Chipmunk建造的,所以总体速度将非常相似,只有当信息从Chipmunk传递/传递到Chipmunk时才会有所不同。这正是微基准测试要衡量的开销类型。
《Pymunk》没有《金龟子》功能齐全,所以为了和《金龟子》相比,我们必须做一些调整。一个主要的区别是,它不实现 position_func 函数,因此我们使用冲突处理程序执行替代回调测试::
h = s.add_default_collision_handler()
def f(arb):
return false
h.pre_solve = f
s.step(0.01)
(见 pymunk-collision-callback.py 和 cymunk-collision-callback.py )
结果¶
测试在搭载英特尔i7-4600U的惠普G1 1040笔记本电脑上运行。笔记本电脑运行Windows,测试在运行64位Debian的VirtualBox VM中运行。CPython测试使用来自Conda的CPython,而PyPy测试使用手动下载的PyPy。CFFI版本1.10.0和Cython 0.25.2。
由于Cymunk没有合适的版本,所以我使用了其Github库中最新的主版本,hash 24845cc,检索日期为2017-09-16。
获取:¶
CPython3.5.3 |
PYPY 5.8 |
|
---|---|---|
Pymunk5.3 |
2.14s |
0.33s |
《西蒙克20170916》 |
0.41s |
(10秒) |
冲突-回调:¶
CPython3.5.3 |
PYPY 5.8 |
|
---|---|---|
Pymunk5.3 |
3.71s |
0.58s |
《西蒙克20170916》 |
0.95s |
(7.01秒) |
(括号内的Cymunk结果显示在PyPy上,因为众所周知,Cython在PyPy上速度很慢)
从这些结果中我们可以看到,Cymunk在CPython上的速度比在CPython上的Pymunk快得多,但当我们包括PyPy时,Pymunk取得了全面的胜利。
我们没有考虑到的一点是,您可以牺牲便利性来换取性能,并在应用程序代码中使用Cython来加快速度。我认为这就是KivEnt使用的方法,KivEnt是Cymunk的主要用户。然而,由于编译器要求和代码更改,在开发应用程序时需要进行复杂得多的设置。