在收听Stack Overflow播客时,一直不断地谈到“真正的程序员”使用C编写,并且C之所以更快是因为它“靠近机器”。将前一个论点留给另一个帖子,那么C有什么特别之处使其比其他语言更快呢?
换句话说,有什么可以阻止其他语言编译成二进制代码并像C一样快速运行?
在收听Stack Overflow播客时,一直不断地谈到“真正的程序员”使用C编写,并且C之所以更快是因为它“靠近机器”。将前一个论点留给另一个帖子,那么C有什么特别之处使其比其他语言更快呢?
换句话说,有什么可以阻止其他语言编译成二进制代码并像C一样快速运行?
C语言设计者做出了一个权衡。也就是说,他们决定将速度放在安全性之上。C语言不会:
当你索引到一个数组时,在Java中需要进行一些虚拟机方法调用、边界检查和其他合理性检查。这个过程是有效的且完全正确的,因为它在应该有安全性的地方增加了安全性。但是在C语言中,甚至相当琐碎的事情也没有安全保障。例如,C语言不要求memcpy检查要复制的区域是否重叠。它并不是作为编写大型商业应用程序的语言而设计的。
但是,这些设计决策并不是C语言的缺陷。它们是经过设计的,因为它允许编译器和库编写者从计算机中获得每一位性能。下面是C语言精神的描述,C语言基本原理文件C Rationale中解释了这一点:
C代码可能不可移植。虽然它努力为程序员提供编写真正可移植程序的机会,但委员会并不想强制程序员编写可移植程序,以避免使用C作为“高级汇编语言”:编写特定于机器的代码是C的优点之一。
保持C的精神。委员会将保存传统C语言精神作为主要目标之一。C精神有许多方面,但其本质是C语言基于的基本原则的社区情感。 C精神的某些方面可以用以下短语概括:
- 相信程序员。
- 不要阻止程序员做必须做的事情。
- 保持语言小而简单。
- 只提供执行操作的一种方式。
即使不能保证可移植性,也要使其快速。 最后这句谚语需要一点解释。高效代码生成的潜力是 C 语言最重要的优势之一。为了确保不会因为看似非常简单的操作而导致代码爆炸,许多操作被定义为按目标机器硬件处理而不是按一般抽象规则处理。例如,在扩展用于表达式的 char 对象的规则中,可以看到愿意接受计算机所做的事情的规则:char 对象的值扩展为有符号或无符号量通常取决于在目标机器上哪个字节操作更有效率。
function RemoveAllAFromB(a, b: string): string;
var
before, after :string;
begin
Result := b;
if 0 < Pos(a,b) then begin
before := Copy(b,1,Pos(a,b)-Length(a));
after := Copy(b,Pos(a,b)+Length(a),Length(b));
Result := before + after;
Result := RemoveAllAFromB(a,Result); //recursive
end;
end;
在 C 中我写下了这段代码:
char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (s1[i] == s2[j]) {
break;
}
}
if (j == len2) { /* s1[i] is not found in s2 */
*result = s1[i];
result++; /* assuming your result array is long enough */
}
}
但是C语言版本中有多少个优化呢?我们在实现过程中做出了许多关于实现的决策,这些决策在Delphi版本中我并没有考虑。字符串是如何实现的呢?在Delphi中我看不到它。在C语言中,我决定它将是指向ASCII整数数组的指针,我们称之为字符。在C语言中,我们逐个测试字符是否存在。在Delphi中,我使用Pos函数。我没有看到这句话,所以我会说:C语言往往更快,因为几乎所有其他语言都是用C语言编写的。
Java是基于C语言构建的,Python是基于C(或Java、.NET等)构建的,Perl也是如此。操作系统是用C语言编写的,虚拟机是用C语言编写的,编译器是用C语言编写的,解释器是用C语言编写的。有些东西仍然是用汇编语言编写的,这往往更快。越来越多的东西正在用其他东西编写,这个东西本身就是用C语言编写的。
你在其他语言(不是汇编语言)中编写的每个语句通常在底层实现时都被实现为几个C语句,并编译成本地机器码。由于这些其他语言往往存在的目的是为了获得比C语言更高级别的抽象,因此在C语言中需要额外的语句集中在添加安全性、添加复杂性和提供错误处理上。这些通常是好事,但它们有一个代价,它的名字是速度和大小。
个人而言,我已经用过几十种语言,涵盖了大部分可用的范围,我个人一直在寻找你所提到的魔法:
我怎么能做到两全其美?我怎么能在我喜欢的语言中玩弄高级别的抽象,然后为了速度降到C语言的细节层次?
经过几年的研究,我的答案是Python(基于C语言)。你可能想看看。顺便说一句,你也可以从Python降到汇编语言(需要一些特殊库的帮助)。
另一方面,任何语言都可以编写糟糕的代码。因此,C(或汇编)代码并不自动更快。同样,一些优化技巧可以使高级语言代码的部分接近原始C代码的性能水平。但是对于大多数应用程序来说,程序在等待人或硬件时花费了大部分时间,所以这种差异确实无关紧要。
享受吧。
这里有很多问题,大部分我都没有资格回答。但对于最后一个问题:
其他语言为什么不能编译成二进制并且与C一样快?
简单来说,是抽象层级。
C只有一到两个抽象层级远离机器语言。Java和.NET语言至少有三个抽象层级远离汇编语言。Python和Ruby的情况我不太确定。
通常,程序员使用的越多的玩具(比如复杂的数据类型等),你距离机器语言就越远,需要进行更多翻译。
我的回答可能有些偏差,但基本意思就是这样。
这篇文章有一些很好的评论,提供了更多细节信息。
我猜你忘记了汇编语言也是一种语言 :)
但说真的,只有当程序员知道自己在做什么时,C程序才会更快。你可以很容易地编写一个C程序,其运行速度比使用其他语言编写的执行相同任务的程序还慢。
C语言之所以更快,是因为它是设计成这样的。它允许你进行很多“低级别”的操作,从而帮助编译器优化代码。或者说,由你作为程序员来负责优化代码。但这通常非常棘手和容易出错。
其他语言,如先前提到的其他语言,更注重程序员的生产率。普遍认为程序员的时间比机器时间(即使在旧时代)要昂贵得多。因此,尽量减少程序员花费在编写和调试程序上的时间而不是程序的运行时间,这是非常有意义的。为了实现这个目标,你将牺牲一些可以使程序更快的内容,因为很多事情都被自动化了。