我问这个是因为我使用 Python,但它也适用于其他解释语言(Ruby、PHP、JavaScript)。
每当我在代码中留下评论时,我是否会减慢解释器的速度?根据我对解释器的有限理解,它将程序表达式作为字符串读取,然后将这些字符串转换为代码。似乎每次解析评论时,都是在浪费时间。
是这样吗?解释语言中的注释是否有一些约定,或者效果可以忽略不计?
我问这个是因为我使用 Python,但它也适用于其他解释语言(Ruby、PHP、JavaScript)。
每当我在代码中留下评论时,我是否会减慢解释器的速度?根据我对解释器的有限理解,它将程序表达式作为字符串读取,然后将这些字符串转换为代码。似乎每次解析评论时,都是在浪费时间。
是这样吗?解释语言中的注释是否有一些约定,或者效果可以忽略不计?
对于 Python 来说,源文件在执行之前被编译(.pyc
文件),并且注释在这个过程中被剥离。因此,如果您有大量注释,它们可能会减慢编译时间,但它们不会影响执行时间。
好吧,我写了一个这样的简短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 的注释会产生可衡量的差异。
注释通常在解析阶段或之前被剥离,并且解析非常快,因此有效地注释不会减慢初始化时间。
对于日常使用,效果可以忽略不计。它很容易测试,但是如果您考虑一个简单的循环,例如:
For N = 1 To 100000: Next
您的计算机可以比您眨眼更快地处理(数到 100,000)。忽略以某个字符开头的一行文本将快 10,000 倍以上。
别担心。
这取决于解释器的实现方式。大多数合理的现代解释器在任何实际执行之前至少对源代码进行一些预处理,这将包括剥离注释,以便它们从那时起没有任何区别。
曾经,当内存受到严重限制时(例如,64K 总可寻址内存和用于存储的盒式磁带),您不能认为这样的事情是理所当然的。在 Apple II、Commodore PET、TRS-80 等的时代,程序员显式删除注释(甚至空白)以提高执行速度是相当常规的。这也只是当时1例行使用的众多源代码级黑客攻击之一。
当然,这些机器的 CPU 一次只能执行一条指令,时钟速度约为 1 MHz,并且只有 8 位处理器寄存器,这也有所帮助。即使是你现在只能在垃圾箱里找到的机器也比那些机器快得多,以至于它甚至都不好笑......
1. 再举一个例子,在 Applesoft 中,您可能会获得或损失一点速度,具体取决于您对行进行编号的方式。如果没记错的话,速度增益是当 goto 语句的目标是 16 的倍数时。
编写了一个像 Rich's 这样的脚本并带有一些评论(只有大约 500kb 的文本):
# -*- 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
根据大卫的评论编辑:
-*- 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
我对解释器的有限理解是它将程序表达式作为字符串读取并将这些字符串转换为代码。
大多数解释器读取文件中的文本(代码)并生成抽象语法树数据结构,因为它可以很容易地被下一阶段的编译读取。该结构不包含文本形式的代码,当然也没有注释。仅仅这棵树就足以执行程序。但是解释器出于效率原因,会更进一步并生成字节码。而 Python 正是这样做的。
我们可以说,
当程序运行时,您编写的代码和注释根本不存在。
所以不,注释不会在运行时减慢程序的速度。
注意:不使用其他内部结构来表示文本以外的代码(
即语法树)的解释器必须完全按照您提到的方式进行。在运行时一次又一次地解释代码。
有注释会减慢启动时间,因为脚本会被解析成可执行的形式。但是,在大多数情况下,注释不会减慢运行时间。
此外,在 python 中,您可以将 .py 文件编译成 .pyc,其中不包含注释(我希望如此)——这意味着如果脚本已经编译,您也不会获得启动机会。
我想知道如何使用评论是否重要。例如,三引号是一个文档字符串。如果您使用它们,则内容将被验证。不久前,我在将库导入到 Python 3 代码中时遇到了一个问题……我在 \N 上遇到了这个关于语法的错误。我查看了行号,它是三引号注释中的内容。我有些惊讶。Python 新手,我从没想过块注释会被解释为语法错误。
只需键入:
'''
(i.e. \Device\NPF_..)
'''
Python 2 不抛出错误,但 Python 3 报告: SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 14-15: malformed \N character escape
所以 Python 3 显然在解释三引号,确保它是有效的语法。
但是,如果变成单行注释:#(即\Device\NPF_..)
没有错误结果。
我想知道是否将三引号注释替换为单行,是否会看到性能变化。
正如其他答案已经说明的那样,像 Python 这样的现代解释语言首先将源代码解析并编译为字节码,而解析器则简单地忽略注释。这显然意味着任何速度损失只会在启动时实际解析源时发生。
因为解析器忽略了注释,所以编译阶段基本上不受你输入的任何注释的影响。但是注释本身的字节实际上是被读入的,然后在解析过程中被跳过。这意味着,如果您有大量的注释(例如数百兆字节),这会减慢解释器的速度。但是话又说回来,这也会减慢任何编译器的速度。
这个问题真的很老,但是在阅读了声称它不会影响执行时间的公认答案之后,这是错误的,我给你一个简单的例子,你可以看到并检查它确实影响执行时间的数量。
我有一个名为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 毫秒,因此慢了大约 7 倍。
因此,是的,如果您有很多评论,它会影响执行时间。