查看原文
其他

“一百万行Python代码对任何人都足够了”

CSDN App AI科技大本营 2020-02-12

作者 | Jake Edge
译者 | Kolen
出品 | AI科技大本营(ID: rgznai100)
 
编程语言通常对其操作过程的各个方面都有或明或暗的限制。诸如标识符的最大长度或变量可以存储的值的范围之类的事情,这些是相当明显的例子,但是还有其他一些例子,其中许多是语言设计者未指明的,并且出现在各种实际应用语言编程的过程中。

这种模棱两可的限制可能会带来严重后果,因此在Python中确定各种各样的限制是“Python-dev”邮件列表(译者注:“Python-dev”是Python开发者社区的一个子栏目)上正在持续进行的讨论目标。
 
Mark Shannon发表了一项“对Python程序的各个方面(比如每个模块的代码行)实行一百万行限制”的提案。一百万看起来像是一个任意的数字(事实上也是如此),但他提出这一想法还是考虑到这个数字比较容易让人记住,因此可以使得程序员在对编程语言添加的限制而感到疑惑时无需查阅参考。
 
尽管有如此提案的想法,Python虚拟机存储的某些值(例如行号)是32位值,这显然固化了自身的限制,但是这样的值可能会浪费绝大多数一直没有关闭的Python程序的存储空间。除此之外,溢出这些32位值可能会导致安全性和其他类型问题的出现。
 
正如Shannon指出的,21位可以容纳的范围是从-1,000,000到1,000,000,其中三个值可以打包成一个64位字。“内存访问通常是现代CPU性能的一个限制因素。在ALU使用量适度增加(用于移位和屏蔽)的情况下,更好地打包数据结构可以增强局部性并减少内存[带宽]。” 他还指出,基于堆栈框架对象,代码对象和对象本身的数据结构可以通过这种打包的形式而受益。“还有一种潜在的更有效的指令格式,可以加快解释器的分派速度。”
 
他提议将限制用于以下Python程序的七个不同方面:

  • 模块中的源代码行数
  • 代码对象中字节码指令的数量
  • 代码对象的局部变量和堆栈使用量的总和
  • 代码对象中不同名称的数量
  • 代码对象中的常量数
  • 正在运行的解释器中的类别数
  • 正在运行的解释器中的实时协同程序数
 
他还解决了“这不又是类似于‘640K对任何人来说应该已经足够了’?”的问题,这是一个当提出任意限制时,人们会立即想到的问题。他指出,Java虚拟机(JVM)将许多程序元素限制为65535(适合16位);这可能会产生限制,但主要是针对程序生成器,而不是手工编写的代码。他对Python提出的限制远不止于此,他认为这不会成为人为生成代码的真正障碍。他说到,“虽然生成的代码可能会超出限制,但代码生成器很容易修改其输出以符合标准。”


社区成员的一些回应

 
他为一些限制提供了简短的理由,但许多在这篇文章中发表过评论的人关心的是这些限制究竟能提供多少好处。Chris Angelico问Shannon是否做过任何评估,来看看会有多大的影响。Steven D'Aprano同意获得内存和安全性好处的前提,但他期望Python开发人员对预期的速度提高抱有“一点期望”。Steve Dower认为,总体的思路并非不合理,尽管他也有一些担忧:
 
“出于许多原因,选择小于2**32的任意限制肯定更安全,而且不太可能影响实际使用。我们已经有了一些低于10**6的实际限制(例如if/else深度和递归限制)。”
 
“也就是说,我真的不想影响边缘情况的使用,而且我非常熟悉其他任意限制的例子(任何文件系统都不需要超过260个字符的路径,对吧?:o))。”
 
Dower引用Windows MAX PATH值,该值将Win32 API中的路径名限制为最多260个字符。他认为,虽然每个模块的行数限制“感觉是最随意的”,但提出的几个限制似乎确实合理。注释和空行肯定会超过限制,这让他停顿思考了一下。他说,“‘正在运行的解释器中的类’”的有关限制有点令人担忧,但可能有办法处理超出此限制的程序,同时仍能获得它带来的所有收益:“即使没有其他的PEP,这里的好处似乎还是值得的。”
 
但Rhodri James认为,像这样提出的限制最终将成为某一部分人的问题;他也反对将计数打包成更小的比特宽度的想法,因为每次访问的掩蔽和移位效率均较低。Gregory P. Smith通常赞成限制,但他担心代码生成会与限制发生冲突;他指出,JVM限制在Android领域是一个大问题。线程中的其他人也指出JVM限制是有问题的。
 
Guido van Rossum对这个想法发表了一些的看法。他对所要解决的问题有些疑惑,并表示怀疑,例如,以20位而不是32位表示行号确实会大大提高效率。他担心“给生成代码带来麻烦的现有的(或偶然出现的)限制”。但他还指出,现有的CPython解析器只限于100个嵌套括号级别(以及可能还有嵌套的缩进级别),而且他没有听到过任何相关的吐槽。
 
Oscar Benjamin提到了一个具有一百万行的文件的真实案例。这是一个SymPy的测试文件,该文件使Python 3.6解释器崩溃了(尽管在3.7.1中已修复)。实际的测试大约只有3000行,但是pytest框架将其重写为一个包含一百万行的文件。本杰明还指出,一百万个字节码指令的限制更加严格。
 
Jim J. Jewett说,应该在语言本身的限制和CPython实现的限制之间进行区分。他指出“记录CPython选择实施的限制(特别是当前的选择),有很大的价值。” 但是,对语言本身来说,将限制设置得太高可能会遗漏MicroPython之类的实现。
 
“更改CPython(或至少在默认模式下为CPython)所支持的限制或其字节码格式可能很有价值,但应将其清楚地表述为CPython实现PEP(或字节码PEP),而不是一个语言更改PEP。”

PEP 611


Shannon接受了反馈,并将其反映在PEP611(“百万限制”)中。这就引出了另一个话题,在这个话题中,人们呼吁对变化进行某种基准测试,以便合理地评估变化。Barry Warsaw还说“对于你是建议使用Python作为语言限制,还是建议使用CPython作为实现限制,还缺乏明确性”。如果限制是针对语言的,“那么PEP需要声明,并且应该请求其他实现开发人员的反馈”。
 
几天后,Shannon要求评论人士更准确地表达他们的关心的问题。任一选择都有相关的成本,但仅简单地说明对更高限额的偏好并不完全有帮助:
 
“仅仅说你想要更大的限制是没有意义的。如果不需要为任意大的限制付出任何代价,那么我就不会首先提出PEP。”
 
“更高限额的成本由所有人承担,但收益却很少,请注意这一点。”
 
Angelico再次要求提供具体数字,但是Shannon说,性能的提高很难量化:“ 鉴于将启用的潜在优化数量无限,因此在数字上加一个数字有点困难:) ”。不出所料,这种回答并不完全受欢迎。但是部分问题可能是Shannon将潜在的优化部分地看作是限制所带来的其他优势的副产品。正如Nathaniel Smith所说:
 
“Mark,可能你想重新构造PEP,使其更像是‘这对正确性有好处,并且可以为解释器提供可靠的推理,这有很多好处(并且速度最终可能是其中之一)’我的印象是,你将加速视为次要动机,而其他人则感觉到加速是整个动机,因此,一种或多种方式会使人们感到困惑。”
 
Shannon说,做出这种改变当然需要理由,但他认为,目前的限制(或缺乏限制)是由于“历史事故和/或实施细节”造成的,这理由似乎很空洞。Van Rossum对这一说法持异议:
 
“哇哦,在现状中没有局限性(除了通过可用的内存间接地限制各种事物之外)最有可能是有意决策的结果。‘没有任意限制’是Python最初设计理念的一部分。我们并非总是成功(想到树的深度和调用递归深度),但这绝对是哲学。”
 
“你非常需要证明为什么我们现在应该改变。“无数的潜在优化”并没有消除它。“
 
Shannon回答说,这可能是语言哲学的一部分,“但实际上Python有很多限制。”例如,他指出,在一个代码对象中有231条以上的指令将导致CPython崩溃;这是一个可以修复的错误,但这类问题可能很难测试和查找。
 
“显式的限制更容易测试。超出限制的代码是否会以预期的方式失败并且恰好在限制以下的代码可以正常工作?”
 
“我想要的是允许更有效地使用资源,而又不会出现较低或未指定的限制。有限的机器上总会有一些限制。如果未指定它们,它们仍然存在,我们只是不知道它们是什么或它们如何表现出来。”
 
Van Rossum特别提出了对类数和协同程序数的限制,因为这是有问题的。更改对象标头(这是对类的限制所允许的一件事)是对C API的更改,因此他认为应该单独讨论。协同程序“只是另一个Python对象,没有与之相关联的操作资源”,因此他不明白为什么要专门针对它们。其他人同意协同工作,并提出了可能需要超过一百万个应用程序的建议。这些反对意见导致Shannon放弃了PEP中的协同程序,因为“限制协同程序的理由可能是最弱的”。

指导委员会的想法

 
在PEP上, Python指导委员会(可能是其最后一次正式行动之一,因为在12月16日选举产生了新的理事会)。华沙表示,已经在12月10日的会议上进行了讨论。理事会建议将PEP分为两部分,一部分适用于该语言的所有实现,并将提供在运行时确定限制的方法;另一部分是针对CPython的特定实现,但对该实现有限制。此外,“我们鼓励PEP的作者和支持者收集实际的绩效数据,以帮助我们评估PEP是否是一个好主意。”
 
Shannon仍然怀疑用“少量优化”来判断PEP是正确。不可能对所有无限可能的优化都加上数字,但也“不可能完美地预测哪些限制可能限制未来的可能的应用”。不过,他也同意,提出受限制的示例优化和应用程序将很有用。
 
在另一条主题中,Shannon要求提供反馈意见和建议,并提供具体细节。他还想知道,他选择一个数字作为限制(主要是作为记忆的辅助手段)的想法是否重要。又一些人认为,由于各种原因选择一个数字是不合理的。正如D' Aprano所说的那样,没有人需要记住这些限制,因为它们无论如何都应该很少会受到限制,而且应该有一种在运行时获得它们的方法。除此之外,对于嵌套括号、递归深度等现在还没有、将来也不会用到一百万行。
 
Paul Moore也认为单一的限制值并不重要,尽管他赞成为任何限制选择整数,而不是根据实现细节进行选择。他可能还总结了大多数人是如何看待这个想法的:
 
“我的观点是,我反对任何没有显示出好处的限制。我不在乎有多少好处,尽管我希望限制的影响与好处的水平保持一致。实际上,这意味着我认为这个提议是落伍的。我更希望这项提案的定义是‘如果我们强加这样或那样的一个限制,我们将能得到一个收益’。”
 
这就是现在的情况。指导委员会似乎对这个一百万行代码的限制很感兴趣,因为它在初期就接受了PEP,尽管该委员会的利益可能与Shannon的观点并不完全一致。然而,定义不清的限制,以及如果超过这些限制会发生什么的不明确语义,将对任何人都没有什么好处。对CPython的规范和对整个语言的其他API进行一些收紧将是很好的一步。它可能允许那些感兴趣的人在CPython的实验分支中调整值,以测试一些优化可能性。

原文链接:https://lwn.net/Articles/807218/

(*本文为AI科技大本营编译文章,转载请微信联系1092722531)


精彩推荐



点击阅读原文,或扫描文首贴片二维码


所有CSDN 用户都可参与投票和抽奖活动

加入福利群,每周还有精选学习资料、技术图书等福利发送


推荐阅读

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存