当前位置: 首页 > 面试题库 >

Python性能特征

景建业
2023-03-14
问题内容

我正在调整我的一个宠物项目以提高其性能。我已经淘汰了探查器以识别热点,但是我认为对Python性能特性的了解要好一些,这将非常有用。

我想知道几件事:

一些现代的编译器拥有非常聪明的优化器,它们通常可以采用简单的代码,并使其运行速度比任何人工调整代码的尝试都要快。根据优化器的智能程度,使我的代码“哑巴”可能更好。

尽管Python是一种“解释”语言,但它确实可以编译为某种形式的字节码(.pyc)。这样做有多聪明?

  • 它会折叠常数吗?
  • 它会内联小型函数还是展开短循环?
  • 它会执行复杂的数据/流分析吗?我没有资格正确解释。

以下操作的速度(相对而言)

  • 函数调用
  • 类实例化
  • 算术
  • “较重”的数学运算,例如sqrt()

内部如何处理数字?

数字如何存储在Python中。它们是存储为整数/内部浮动还是作为字符串移动?

NumPy

NumPy可以带来多少性能差异?此应用程序大量使用向量和相关数学。通过使用它来加速这些操作,可以产生多大的差异。

还有其他有趣的事

如果您能想到其他任何值得了解的内容,请随时提及。

一些背景…

由于有一些人提出“先看算法”建议(这是非常明智的建议,但对我问这个问题的目的并没有真正的帮助),因此我将在此处添加一些内容,以及为什么我要问这个

有问题的宠物项目是用Python编写的光线跟踪器。距离还不太远,目前仅对场景中的两个对象(一个三角形和一个球体)进行了测试。不会执行任何阴影,阴影或照明计算。该算法基本上是:

for each x, y position in the image:
     create a ray
     hit test vs. sphere
     hit test vs. triangle
     colour the pixel based on the closest object, or black if no hit.

光线跟踪中的算法优化通常通过尽早消除场景中的对象来起作用。它们可以为复杂的场景提供可观的提升,但是如果此光线示踪剂无法在不费力的情况下仅对两个对象进行测试,那么它将根本无法处理很多事情。

尽管我意识到基于Python的光线跟踪器无法完全达到基于C的光线跟踪器的性能,但考虑到像Arauna这样的实时光线跟踪器可以在我的计算机上管理15-20
FPS,从而以640x480渲染相当复杂的场景,我希望可以在一秒钟内用Python渲染非常基本的500x500图像。

目前,我的代码需要38秒。在我看来,这确实不需要花那么长时间。

分析显示了在这些形状的实际命中测试例程中花费的大部分时间。在光线跟踪器中,这并不出乎我的意料。这些命中测试的调用次数分别为250,000(确切为500x500),这表明调用它们的次数与应有的次数完全相同。这是3%的漂亮教科书案例,建议进行优化。

我计划在完善代码的同时进行完整的计时/测量。但是,如果没有一些有关Python成本的基本知识,我调优我的代码的尝试只会使他们无所适从。我认为这对我很有帮助,以获取一点知识为路。


问题答案:

Python的编译器特意是简单的-
这使其变得快速且高度可预测。除了不断进行折叠之外,它基本上会生成忠实地模仿您的源代码的字节码。有人已经建议使用dis,这确实是查看所获得的字节码的好方法-例如,for i in [1, 2, 3]:实际上是如何不进行常量折叠而是动态生成文字列表,而for i in (1, 2, 3):(在文字元组上循环而不是文字列表)
能够固定倍(原因:一个列表是可变对象,并保持了“污垢简单的”任务书的编译器不会刻意去检查该特定列表从未如此修改它 可以 被优化成一个元组)。

因此,有足够的空间进行手动微优化-特别是吊装。即重写

for x in whatever():
    anobj.amethod(x)

f = anobj.amethod
for x in whatever():
    f(x)

保存重复的查找(编译器不会检查的运行是否anobj.amethod实际上可以更改anobj的绑定&c,以便下次需要进行新的查找-它只是做简单的事情,即无需吊装,这可以保证正确性,但绝对不能保证速度飞快;-)。

该timeit模块(在shell提示下恕我直言,最好使用),使得它非常简单的测量汇编+字节码解释的总体影响(只是确保片断你测量有没有副作用会影响时序,因为timeit
跑过来并循环成一圈;-)。例如:

$ python -mtimeit 'for x in (1, 2, 3): pass'
1000000 loops, best of 3: 0.219 usec per loop
$ python -mtimeit 'for x in [1, 2, 3]: pass'
1000000 loops, best of 3: 0.512 usec per loop

您可以看到重复列表构建的成本-并尝试进行一些细微调整来确认这确实是我们正在观察的结果:

$ python -mtimeit -s'Xs=[1,2,3]' 'for x in Xs: pass'
1000000 loops, best of 3: 0.236 usec per loop
$ python -mtimeit -s'Xs=(1,2,3)' 'for x in Xs: pass'
1000000 loops, best of 3: 0.213 usec per loop

将iterable的构造移至-s设置(仅运行一次且不定时)表明,在tuple上,适当的循环会稍快一些(可能是10%),但是第一对的问题就大了(列表比tuple慢了100%以上)
)主要与建筑相关。

手持timeit和编译器是故意在其优化很简单的头脑的知识,我们可以很容易地回答你的其他问题:

以下操作的速度(相对而言)

* Function calls
* Class instantiation
* Arithmetic
* 'Heavier' math operations such as sqrt()
$ python -mtimeit -s'def f(): pass' 'f()'
10000000 loops, best of 3: 0.192 usec per loop
$ python -mtimeit -s'class o: pass' 'o()'
1000000 loops, best of 3: 0.315 usec per loop
$ python -mtimeit -s'class n(object): pass' 'n()'
10000000 loops, best of 3: 0.18 usec per loop

因此,我们看到:实例化一个新类并调用一个函数(均为空)的速度大致相同,实例化可能具有很小的速度余量,可能为5%;实例化一个老式类最慢(大约50%)。当然,微小差异(例如5%或更少)可能是噪音,因此建议重复尝试几次;但是像50%这样的差异绝对远远超出了噪音。

$ python -mtimeit -s'from math import sqrt' 'sqrt(1.2)'
1000000 loops, best of 3: 0.22 usec per loop
$ python -mtimeit '1.2**0.5'
10000000 loops, best of 3: 0.0363 usec per loop
$ python -mtimeit '1.2*0.5'
10000000 loops, best of 3: 0.0407 usec per loop

在这里我们看到:调用sqrt要比由操作员执行相同的计算(使用**提高功率的操作员)慢,这要花大约调用空函数的代价;所有算术运算符在噪声范围内的速度大致相同(3或4纳秒的微小差异绝对是噪声;-)。检查连续折叠是否会干扰:

$ python -mtimeit '1.2*0.5'
10000000 loops, best of 3: 0.0407 usec per loop
$ python -mtimeit -s'a=1.2; b=0.5' 'a*b'
10000000 loops, best of 3: 0.0965 usec per loop
$ python -mtimeit -s'a=1.2; b=0.5' 'a*0.5'
10000000 loops, best of 3: 0.0957 usec per loop
$ python -mtimeit -s'a=1.2; b=0.5' '1.2*b'
10000000 loops, best of 3: 0.0932 usec per loop

…我们发现确实是这样:如果将一个或两个数字作为变量(阻止不断折叠),我们将付出“现实”的代价。变量查找具有其自身的成本:

$ python -mtimeit -s'a=1.2; b=0.5' 'a'
10000000 loops, best of 3: 0.039 usec per loop

无论如何,当我们试图测量如此微小的时刻时,这绝非微不足道。确实, 持续 查找也不是免费的:

$ python -mtimeit -s'a=1.2; b=0.5' '1.2'
10000000 loops, best of 3: 0.0225 usec per loop

如您所见,虽然它比变量查找小,但却是相当可比的-大约一半。

如果(何时进行)(经过仔细的性能分析和测量)您决定对计算的某些核心进行迫切的优化,我建议您尝试cython-这是C
/
Python合并,它试图与Python一样简洁,与C一样快,而它无法做到100%肯定可以胜任(特别是,它使二进制代码比您的前代语言pyrex获得的速度要快得多,并且比它的功能还要丰富)
。对于最后几%的性能,您可能仍想使用C(在某些特殊情况下为汇编/机器代码),但这确实非常罕见。



 类似资料:
  • Selenium有以下功能特性: Selenium是一个开源和可移植的Web测试框架。 Selenium IDE为创作测试提供了回放和录制功能,而无需学习测试脚本语言。 它可以被视为领先的基于云的测试平台,可帮助测试人员记录他们的操作并将其导出为可重复使用的脚本,并具有易于理解且易于使用的界面。 Selenium支持各种操作系统,浏览器和编程语言。如下列表: 编程语言: C# ,Java,Pyth

  • 6.2 功能特性 SpEL支持以下的一些特性: 字符表达式 布尔和关系操作符 正则表达式 类表达式 访问properties,arrays,lists,maps等集合 方法调用 关系操作符 赋值 调用构造器 Bean对象引用 创建数组 内联lists 内联maps 三元操作符 变量 用户自定义函数 集合投影 集合选择 模板表达式

  • 为了与C语言标准保持高度的兼容性,在C标准委员会的协助之下,一些细小的改变被引入到C++0x中。 long long 扩展的整型数据类型(例如,关于可选的更长的整型数的规则) 关于UCN的改变[N2170==07-0030]: 解除了”字符常量/字面字符串中不得使用控制/基本的通用字符名”的限制 // 译注: C++03中允许通过\uNNNN的形式 // 在字符/字符串中引入非ASCII字符(Un

  • 1 Python的函数参数传递 看两个例子: a = 1 def fun(a): a = 2 fun(a) print a # 1 a = [] def fun(a): a.append(1) fun(a) print a # [1] 所有的变量都可以理解是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。 通过id来看引用a的内存地址可以比较理解: a = 1

  • Selenium WebDriver一些最重要的功能特性是: 多浏览器支持 :Selenium WebDriver支持各种Web浏览器,如Firefox,Chrome,Internet Explorer,Opera等等。它还支持一些非传统或罕见的浏览器,如HTMLUnit。 多编程语言支持:WebDriver还支持大多数常用的编程语言,如Java,C#,JavaScript,PHP,Ruby,Pe

  • 主要内容:1.菜单栏,2. 工具栏,3. 地址栏,4. 测试用例窗格,5.测试脚本编辑器框,6. 开始/停止录制按钮,7. 日志,引用窗格Selenium IDE分为不同的组件,每个组件都有自己的特性和功能。这里对Selenium IDE的七个不同组件进行了分类,其中包括: 菜单栏 工具栏 地址栏 测试案例窗格 测试脚本编辑器框 开始/停止录制按钮 日志,引用窗格 现在,我们将详细介绍每个组件的特性和功能。 1.菜单栏 菜单栏位于Selenium IDE界面的最顶部。 最常用的菜单栏模块包括: