弱类型是性能提升还是降低?

9
写解释: 在编写解释型语言时,弱类型和强类型哪个更快?
我之所以会有这个想法,是因为目前市面上速度比较快的动态类型解释型语言(如Lua、Javascript)以及大多数解释型语言都采用弱类型。
但是另一方面,强类型可以提供弱类型无法保证的保障,那么,其中一个是否可能具有不可能在另一个中实现的优化技术呢?
严格类型指没有类型之间的隐式转换。例如,在强类型中这将是非法的,但在弱类型的语言中可能是合法的: "5" * 2 == 10。特别是Javascript因这些类型转换而臭名昭著。

1
一个有趣的问题,尽管我担心很难一般性地回答。我期待着关于解释器优化的详细信息。 - user395760
只是为了明确,你所说的弱类型和强类型是什么意思?(我尽量避免使用这些术语,原因在此处给出 - 人们以许多不兼容的方式使用它们。) - DSM
@Jivings JS仍然是动态类型的,因此在编译期间你不能真正确定任何类型(特别是当编译成字节码时 - 你确定你不是在谈论JIT编译器吗?)。最好的情况是猜测某些东西通常会是某种类型,并基于该假设生成代码。但你仍然需要保护条件(检查假设是否正确的条件)和一个更一般/不太优化的版本作为备用(当它不正确时)。但这些都与这个问题无关,因为它是由动态类型引起的,而不是由弱/强类型引起的。 - user395760
那么哪些解释型语言具有强类型? - andrew cooke
1
这个问题有两个方面都不太合理。首先:当进行隐式转换时,会产生额外的开销。一个不执行任何此类隐式转换的程序,无论语言是否支持它们,都将是相同的。因此,你的问题就像在问具有内置反正切函数的编程语言是否比不具有该函数的语言更慢。其次:动态类型语言和静态类型语言中隐式转换的实现方式非常不同;因此,即使你的问题有一个连贯的答案,也取决于所使用的语言类型。 - ruakh
显示剩余10条评论
2个回答

3
在我看来,由于缺乏“强类型解释语言”(使用我从问题评论中理解的定义),这个问题似乎很难用明确的例子回答。
我想不出任何一种解释型语言没有隐式转换。我认为有两个原因:
1.解释型语言通常不是静态类型的。我认为这是因为如果你要实现一个静态类型的语言,那么在历史上,编译相对容易,并且可以给你带来显著的性能优势。
2.如果一种语言没有静态类型,则被迫进行隐式转换。否则,程序员的生活会变得太艰难(他们必须跟踪源代码中看不见的类型,以避免运行时错误)。
因此,在实践中,所有解释型语言都是弱类型的。但是,关于性能增加或减少的问题意味着与某些不同的现有实现策略进行比较。至少,如果我们想讨论不同的现有实现策略,就会这样做。
现在你可能会回答“好吧,想象一个”。好的。那么你就在问运行时检测需要转换的代码和程序员显式添加转换的代码之间的性能差异。在这种情况下,你正在比较动态检测转换需求并调用程序员指定的显式函数之间的差异。
表面上看,检测总是会增加一些开销(在一个(后)编译语言中,这可以通过jit来缓解,但你正在问解释器)。但是,如果你想要快速失败的行为(类型错误),那么即使显式转换也必须检查类型。因此,在实践中,我想差异相对较小。
而这反过来又回到了最初的观点——由于弱类型的性能成本很低(在问题中给出所有其他约束/假设),而替代方案的可用性成本很高,大多数(全部?)解释型语言都支持隐式转换。
抱歉如果我还是没有理解。我担心我可能错过了什么,因为问题——以及这个答案——似乎不是很有趣...
[编辑:也许更好的提问方式是“动态(后期绑定?)语言处理类型转换的各种方法具有哪些比较优势/劣势?”因为我认为你可以争论Python的方法特别强大(表达力强),同时与其他解释型语言具有类似的成本(并且该问题避免了争论Python或任何其他语言是否“弱类型”)。]

Python,正如你所知,几乎没有任何隐式转换。数值类型被提升(单向方式,并且始终从一种类型转换为更通用的类型),并且可以认为if/and/or/not会隐式转换为布尔值(尽管也可以说所有对象都有一个真值,并且这些操作符只是获取该值并处理它 - and/or返回其中一个操作数的事实支持了这种观点)。除此之外,我现在不知道任何隐式转换。而且我怀疑Python在这方面并不孤单。因此,我敢挑战你的第二个理由。 - user395760
通过隐式转换,我指的是像__float__这样的方法的隐式调用 - 请参见http://docs.python.org/reference/datamodel.html#specialnames(因此,将`__nonzero__`转换为布尔值)。 - andrew cooke
Python中一个非常常见的例子是转换为迭代器。这在惯用的Python代码中无处不在,而且以相同的方式实现(__iter__ iirc)。 - andrew cooke
__float__ 不会被隐式调用,只有在 float 中被调用(也许在数字提升期间也会被调用,但我们已经掌握了这个)。__iter__ 确实会被隐式调用,但仅在 for 循环和 {list,dict,set} 推导的上下文中,因此它非常受限制并且总是被调用。出于这个原因,我不认为它是一种隐式转换。而大多数“特殊方法”根本不进行转换 - 它们用于属性访问和运算符重载等目的。 - user395760
__str__ 是另一个常见的方法。如果我没记错,__float__ 会被 __add__ 的默认实现调用。更普遍地说,我不确定你的观点是什么 - 是的,它们模糊了消息分发,因此你可以认为这不是隐式类型转换而是方法调用。这很酷 - 这正是我所说的 Python 有一个好的解决方案,并且争论什么是和什么不是“弱类型”在很大程度上是毫无意义的。如此无意义,以至于我不会再做任何事情。 - andrew cooke
我的观点不是强类型和弱类型是有用的区分,或者我们应该讨论什么算作弱类型。我只是对你的第二个理由(动态类型使得隐式转换必要)表示怀疑。关于你提到的其他例子:__str__ 只被 str 调用(可能是间接的,但肯定是显式的)。正如我们之前都已经说过的那样,算术运算符确实包括一些隐式的提升/转换。 - user395760

1
强类型指的是类型之间没有隐式转换。
问题在于“弱类型”这个术语并没有明确定义,因为有两种非常不同的方式可以进行“隐式转换”,它们对性能产生了几乎相反的影响:
- “脚本语言方式”:值具有运行时类型,当操作需要不同类型时,语言会隐式地应用语义规则来进行类型转换(例如将二进制数格式化为十进制字符串)。这往往会降低性能,因为它需要在运行时存在类型信息,并且需要检查此信息。这两个要求都会引入开销。 - “C方式”:在运行时,一切都只是字节。如果你能说服编译器在字符串上应用一个需要4个字节整数的操作,那么根据你的具体操作方式,该字符串的前4个字节将被简单地视为一个(可能非常大的)整数,或者你会得到一个缓冲区溢出。或者你的鼻子里飞出恶魔。这种方法不需要额外开销,并且可以实现非常快的性能(以及非常壮观的崩溃)。

实际上有三种方法,因为你漏掉了 C 方法:在编译时,表达式具有类型,并且可以插入隐式转换以将一个表达式从一种类型转换为另一种类型。例如,如果 i 是一个 int,那么 5.0 * i 将隐式地将 iint 转换为 double。作出这个转换的决定是在编译时进行的。(你描述的“C 方法”实际上并不是 C 执行隐式转换的方式;相反,它是绕过 C 隐式转换的结果。) - ruakh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接