关于python:评论会减慢解释语言的速度吗?

Do comments slow down an interpreted language?

我之所以这么问是因为我使用的是python,但它也可以应用于其他解释语言(ruby、php、javascript)。

每当我在代码中留下注释时,是否会减慢解释器的速度?根据我对解释器的有限理解,它将程序表达式作为字符串读取,然后将这些字符串转换为代码。似乎每次分析一条评论,都是浪费时间。

是这样吗?对于解释语言的注释是否有一些约定,或者这种影响可以忽略不计?


对于python,源文件在执行之前就被编译了(.pyc文件),并且注释在过程中被剥离。因此,如果您有大量注释,注释可能会减慢编译时间,但它们不会影响执行时间。


好吧,我写了一个这样的短python程序:

1
2
for i in range (1,1000000):
    a = i*10

其思想是,做一个简单的时间负荷计算。

通过计时,运行时间为0.35±0.01秒。

然后我重写了整本詹姆斯国王的圣经,插入如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
for i in range (1,1000000):
   """
The Old Testament of the King James Version of the Bible

The First Book of Moses:  Called Genesis


1:1 In the beginning God created the heaven and the earth.

1:2 And the earth was without form, and void; and darkness was upon
the face of the deep. And the Spirit of God moved upon the face of the
waters.

1:3 And God said, Let there be light: and there was light.

...
...
...
...

Even so, come, Lord Jesus.

22:21 The grace of our Lord Jesus Christ be with you all. Amen.
   """

    a = i*10

这一次需要0.4±0.05秒。

所以答案是肯定的。在一个循环中有4MB的注释会产生可测量的差异。


注释通常在解析阶段或之前被剥离出来,解析速度非常快,因此有效地注释不会减慢初始化时间。


编写了一个类似Rich的脚本,其中包含一些注释(只有大约500kb的文本):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# -*- coding: iso-8859-15 -*-
import timeit

no_comments ="""
a = 30
b = 40
for i in range(10):
    c = a**i * b**i
"""

yes_comment ="""
a = 30
b = 40

# full HTML from http://en.wikipedia.org/
# wiki/Line_of_succession_to_the_British_throne

for i in range(10):
    c = a**i * b**i
"""

loopcomment ="""
a = 30
b = 40

for i in range(10):
    # full HTML from http://en.wikipedia.org/
    # wiki/Line_of_succession_to_the_British_throne

    c = a**i * b**i
"""


t_n = timeit.Timer(stmt=no_comments)
t_y = timeit.Timer(stmt=yes_comment)
t_l = timeit.Timer(stmt=loopcomment)

print"Uncommented block takes %.2f usec/pass" % (
    1e6 * t_n.timeit(number=100000)/1e5)
print"Commented block takes %.2f usec/pass" % (
    1e6 * t_y.timeit(number=100000)/1e5)
print"Commented block (in loop) takes %.2f usec/pass" % (
    1e6 * t_l.timeit(number=100000)/1e5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Scripts>timecomment.py
Uncommented block takes 15.44 usec/pass
Commented block takes 15.38 usec/pass
Commented block (in loop) takes 15.57 usec/pass

C:\Scripts>timecomment.py
Uncommented block takes 15.10 usec/pass
Commented block takes 14.99 usec/pass
Commented block (in loop) takes 14.95 usec/pass

C:\Scripts>timecomment.py
Uncommented block takes 15.52 usec/pass
Commented block takes 15.42 usec/pass
Commented block (in loop) takes 15.45 usec/pass

根据大卫的评论进行编辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 -*- coding: iso-8859-15 -*-
import timeit

init ="a = 30
b = 40
"

for_ ="for i in range(10):"
loop ="%sc = a**%s * b**%s"
historylesson ="""
# <!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"
# blah blah...
# --></body></html>
"""

tabhistorylesson ="""
    # <!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"
    # blah blah...
    # --></body></html>
"""


s_looped = init +"
"
+ for_ +"
"
+ tabhistorylesson + loop % ('   ','i','i')
s_unroll = init +"
"

for i in range(10):
    s_unroll += historylesson +"
"
+ loop % ('',i,i) +"
"

t_looped = timeit.Timer(stmt=s_looped)
t_unroll = timeit.Timer(stmt=s_unroll)

print"Looped length: %i, unrolled: %i." % (len(s_looped), len(s_unroll))

print"For block takes %.2f usec/pass" % (
    1e6 * t_looped.timeit(number=100000)/1e5)
print"Unrolled it takes %.2f usec/pass" % (
    1e6 * t_unroll.timeit(number=100000)/1e5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Scripts>timecomment_unroll.py
Looped length: 623604, unrolled: 5881926.
For block takes 15.12 usec/pass
Unrolled it takes 14.21 usec/pass

C:\Scripts>timecomment_unroll.py
Looped length: 623604, unrolled: 5881926.
For block takes 15.43 usec/pass
Unrolled it takes 14.63 usec/pass

C:\Scripts>timecomment_unroll.py
Looped length: 623604, unrolled: 5881926.
For block takes 15.10 usec/pass
Unrolled it takes 14.22 usec/pass


这取决于解释器是如何实现的。最合理的现代口译员在任何实际执行之前都至少对源代码做了一点预处理,这将包括去掉注释,这样从那一点开始就没有什么区别。

有一次,当内存受到严重限制时(例如,64K总可寻址内存和用于存储的盒式磁带),你不能想当然地认为这类事情是理所当然的。早在苹果II、CommodorePet、TRS-80等时代,程序员就已经相当习惯于显式删除注释(甚至是空白区域),以提高执行速度。这也是当时经常使用的许多源代码级黑客中的一种。

当然,这也有助于那些机器的CPU一次只能执行一条指令,时钟速度在1兆赫左右,并且只有8位处理器寄存器。即使是你现在只在垃圾箱里找到的机器,也比那些机器快得多,甚至都不好笑…

1。另一个例子是,在applesoft中,根据对行进行编号的方式,可能会增加或减少一点速度。如果内存服务,则速度增益是goto语句的目标是16的倍数。


这种效果在日常使用中是可以忽略的。很容易测试,但是如果您考虑一个简单的循环,例如:

1
For N = 1 To 100000: Next

你的电脑能比你眨眼的速度快(数到100000)。忽略以某个字符开头的一行文本将快10000倍以上。

别担心。


My limited understanding of an
interpreter is that it reads program
expressions in as strings and converts
those strings into code.

大多数解释器读取文本(代码)并生成抽象语法树数据结构。这个结构不包含任何代码,文本形式,当然也没有注释。只有这棵树才足以执行程序。但是,由于效率的原因,解释器更进一步,产生字节码。而Python就是这么做的。

我们可以说,代码和注释,以您编写的形式,根本不存在,当程序运行时。因此,注释不会在运行时减慢程序的运行速度。

(*)不使用其他内部结构来表示文本以外的代码的解释程序,语法树,必须完全按照你提到的做。在运行时一次又一次地解释代码。


有注释将减慢启动时间,因为脚本将被解析为可执行形式。然而,在大多数情况下,注释不会减慢运行时的速度。

另外,在python中,您可以将.py文件编译为.pyc,它不包含注释(我希望如此)——这意味着,如果脚本已经编译,您也不会受到启动攻击。


我想知道如何使用评论是否重要。例如,三引号是docstring。如果使用它们,内容将被验证。在我将库导入到python 3代码的时候,我遇到了一个问题…我在上的语法中发现了此错误。我查看了行号,它包含在一个三引号注释中。我有点惊讶。对于Python来说,我从未想过块注释会被解释为语法错误。

如果您键入:

1
2
3
4
'''
(i.e. \Device
PF_..)
'''

python 2不会抛出错误,但python 3报告:syntaxerror:(unicode错误)"unicode escape"编解码器无法解码位置14-15中的字节:格式错误的字符转义

所以python 3显然是在解释三重引号,确保它是有效的语法。

但是,如果转换为单行注释:(即devicepf_..)没有错误结果。

我想知道三个引号的评论是否被单行替换,是否会看到性能变化。


正如其他答案已经指出的那样,像Python这样的现代解释语言首先解析源代码并将其编译为字节码,而解析器只是忽略注释。这显然意味着只有在实际解析源时,才会在启动时发生任何速度损失。

因为解析器忽略注释,所以编译阶段基本上不受您放入的任何注释的影响。但是注释本身的字节实际上正在被读取,然后在解析期间跳过。这意味着,如果您有大量的评论(例如数百兆字节),这将减慢解释器的速度。但同样,这也会减慢任何编译器的速度。