注释会降低解释语言的速度吗?


70

我之所以这样问是因为我使用Python,但是它也可以应用于其他解释语言(Ruby,PHP,JavaScript)。

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

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


3
在我的旧Commodore 64上,这肯定是BASIC中的一个问题。从那时起,语言和硬件都得到了极大的改善。
Fred Larson

6
您应该意识到,“已解释”一词意义不大。Python是字节码编译的,不能直接从源代码中解释。
Thomas Wouters 2010年

考虑到有关此问题的JavaScript可能很有趣。我相信JQuery例如具有剥离注释和多余空格的版本,以最大程度地减少传输时间。
Fred Larson

15
在JavaScript中,删除注释和空格(并尽可能地将内容整理在一起)是很常见的,但实际上并不能加快解析或执行的速度。它是所有关于网络传输时间(和带宽,为繁忙的站点。)
托马斯·沃特斯

4
例如google.com/index.html的来源几乎被混淆了,因为Google已将每个JS变量压缩到最大3个字母,并去除了所有可能的空白。
尼克T

Answers:


89

对于Python而言,源文件(.pyc文件)在执行之前先进行编译,然后在过程中删除注释。因此,如果您有大量注释,则注释可能会减慢编译时间,但它们不会影响执行时间。


36
+1,因为我真的很喜欢gazillion在这种情况下使用
M.威廉姆斯

2
很难想象注释:代码的比率必须要有多高才能被检测到。
Mike Graham

3
@Mike:可能是1 gazillion:1吗?
赛斯·约翰逊

1
不能确定是否有数万亿个兆兆,但我认为您的想法正确。
M. Williams

我只是指出,即使编译时间也只发生一次,然后被缓存。
伊恩·比金

27

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

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

想法是,做一个简单的时间计算负载。

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

然后,我将整个《英王钦定版圣经》插入如下:

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的评论可衡量。


18
在同一篇文章中为科学实验和《圣经》 +1。8vD
Fred Larson

45
那不是评论。这是一个字符串文字。此外,如果您查看两个代码块的实际字节码,则不会有任何区别。该字符串仅被解析一次,完全不参与计算。如果将字符串放在循环外部,则应该看到相同的速度降低。
Thomas Wouters 2010年

14
尽管方法有缺陷,但+1可以抵抗愚蠢的数字投票和用于实际实验的道具。与抽象讨论相比,TIAS(尝试一下)通常提供更好的答案。
3Dave 2010年

6
@David,此测试不是OP描述的情况,也不代表人们实际编写的任何代码。
Mike Graham

3
@Rich,您可以将字符串转换为注释并发布新时间吗?
smci 2011年

19

通常在解析阶段或解析阶段之前删除注释,并且解析速度非常快,因此有效地注释不会降低初始化时间。


10
必须删除注释,因此如果注释足够大,它们将使程序变慢。但是您甚至必须先进行大量评论(MB或GB?),然后才能进行衡量。
亨里克·汉森

3
具有兆字节的注释意味着不止兆字节的代码。实际的分析和编译时间将使“小”注释剥离时间不堪重负。
kennytm

11
我继续尝试了一下。在我的特定测试系统上,解析和执行大约10兆的Python注释(和一条赋值语句)需要349毫秒。在这种情况下,源字节与时间的比率似乎相当恒定,大约为28,000字节/毫秒。Codepad
AKX

好吧,我敢肯定,可以构造出相反的病理例子。哦,看,请看Rich Bradshaw的答案。当然,出于所有实际目的,您是完全正确的。
janneb

5

对于日常使用而言,效果可忽略不计。这很容易测试,但是如果您考虑一个简单的循环,例如:

For N = 1 To 100000: Next

您的计算机可以比眨眼更快地处理(计数到100,000)。忽略以某个特定字符开头的文本行会快10,000倍以上。

不用担心


5

用一些注释(仅约500kb的文本)编写了类似Rich的脚本:

# -*- 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)


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

根据David的评论进行编辑:

 -*- coding: iso-8859-15 -*-
import timeit

init = "a = 30\nb = 40\n"
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 + "\n" + for_ + "\n" + tabhistorylesson + loop % ('   ','i','i')
s_unroll = init + "\n"
for i in range(10):
    s_unroll += historylesson + "\n" + loop % ('',i,i) + "\n"
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)


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

@Nick,我希望任何非幼稚的解释器都只解析循环中第一次通过的注释。您是否通过展开循环或通过在代码中粘贴几百行注释来进行尝试?
3Dave 2010年

4

这取决于解释器的实现方式。最合理的现代解释器在任何实际执行之前都会对源代码进行至少一些预处理,这将包括删除注释,因此从那时起它们没有任何区别。

一次,当内存受到严重限制时(例如64K总可寻址内存,以及用于存储的盒式磁带),您将无法接受这样的事情。早在Apple II,Commodore PET,TRS-80等时代,对于程序员来说,显式删除注释(甚至是空格)以提高执行速度是相当常规的。这也是当时1经常使用的许多源代码级黑客之一。

当然,这也有助于使这些机器的CPU一次只能执行一条指令,时钟速度约为1 MHz,并且只有8位处理器寄存器。甚至您现在只能在垃圾箱中找到的机器也比那些机器快得多,甚至还不有趣...


1.再举一个例子,在Applesoft中,您可能会因行编号方式的不同而有所不同。如果有内存,则速度增加是当goto语句的目标是16的倍数时。


1

注释会减慢启动时间,因为脚本将被解析成可执行文件。但是,在大多数情况下,注释不会减慢运行时间。

此外,在python中,您可以将.py文件编译成.pyc,其中将不包含注释(我希望如此)-这意味着,即使脚本已经编译,也不会受到启动的影响。


s/will slow down the startup time/will slow down the startup time immeasurablys/in most cases comments don't slow down runtime/in all cases comments don't slow down runtime
Mike Graham

1

我对解释器的有限理解是,它以字符串形式读取程序表达式并将这些字符串转换为代码。

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

我们可以说,代码和注释,在你写了他们的形式,仅仅是不存在的
在程序运行时。因此,不,注释不会在运行时降低程序速度。

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


0

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

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


我不确定在严格意义上我会称其为“解释语言”。像动态编译或JIT之类的东西似乎更合适。
3Dave 2010年

0

我想知道评论的使用方式是否重要。例如,三引号是一个文档字符串。如果使用它们,内容将被验证。不久前,我在将库导入到我的Python 3代码中时遇到了一个问题...关于\ N语法的错误。我查看了行号,它是三引号注释中的内容。我有些惊讶。我是Python的新手,我从没想过会因为语法错误而解释块注释。

只需输入以下内容即可:

'''
(i.e. \Device\NPF_..)
'''

Python 2不会引发错误,但是Python 3会报告:SyntaxError:(unicode错误)“ unicodeescape”编解码器无法解码位置14-15的字节:格式错误的\ N字符转义

因此,Python 3显然在解释三引号,以确保它是有效的语法。

但是,如果变成一行注释:#(即\ Device \ NPF_ ..)
没有错误结果。

我想知道是否会看到性能变化,是否用单行替换了三引号注释。


0

这个问题确实很老,但是在阅读了公认的答案后,即它不会影响执行时间(这是错误的)之后,我给您提供一个简单的示例,您可以在其中查看并检查它确实影响执行时间的数量。
我有一个名为的文件constants.py。它在列表中包含国际象棋的所有不同动作:

LABELS = [ "a1b1"
    "a1c1", 
    "a1d1", 
    "a1e1", 
    "a1f1",....]

该列表LABELS包含2272个元素。在另一个文件中,我称:

import constants
np.array(constants.LABELS)

我对其进行了十次测量,代码执行大约需要0.597毫秒。现在,我更改了文件,并在每个元素旁边插入了2272次注释:

LABELS = [ "a1b1",  # 0 
            "a1c1", # 1
            "a1d1", # 2
            "a1e1", # 3
            "a1f1", # 4
             ...,
            "Q@h8", # 2271]

现在,在测量np.array(constants.LABELS)十次执行时间之后,我的平均执行时间为4.28毫秒,因此慢了大约七倍。
因此,是的,如果您有很多注释,它将影响执行时间。


“测试np.array(constants.LABELS)”实际上是什么意思?您看到编译的.pyc文件有什么不同吗?
卢珀·鲁什(Luper Rouch)

@LuperRouch与“测试np.array(constants.LABELS)”我的意思是运行该语句np.array(constant.LABELS)十次并测量该语句的平均执行时间。我将在文本中对此进行澄清。
罗马教皇

您如何运行此语句?也许您可以将测试设置推送到github,以便我们可以看到测试的运行方式,因为看到的差异可能是由于您不重用已编译的.pyc文件(如我所说,注释确实会影响编译)时间,但它们不应影响执行时间)。
Luper Rouch
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.