当前位置:  开发笔记 > 编程语言 > 正文

为什么C这么快,为什么其他语言不快或更快?

如何解决《为什么C这么快,为什么其他语言不快或更快?》经验,为你挑选了16个好方法。

在收听StackOverflow播客时,jab不断出现"真正的程序员"用C语言编写,而C语言更快,因为它"靠近机器".将前一个断言留给另一个帖子,C的特殊之处在于它是否比其他语言更快?或者换一种方式:什么阻止其他语言能够编译成二进制文件,它运行速度和C一样快?



1> coobird..:

关于C没什么特别之处.这就是为什么它快速的原因之一.

较新的语言,支持垃圾收集,动态类型和其他设施,使程序员更容易编写程序.

问题是,存在额外的处理开销,这将降低应用程序的性能.C没有任何这些,这意味着没有开销,但这意味着程序员需要能够分配内存并释放它们以防止内存泄漏,并且必须处理变量的静态类型.

也就是说,许多语言和平台,例如Java(带有Java虚拟机)和.NET(带有公共语言运行时),多年来都有了改进的性能,例如即时编译,从而产生本机机器代码.字节码实现更高的性能.


除了"硬"之外,没有什么能阻止C程序执行相同的分块和垃圾收集.
porneL,手动管理和合理分配在正确使用时总是优于任何GC系统并且给予了很多关注,您对使用模式有绝对的了解,GC没有,加上GC系统增加了开销
垃圾收集可以比手动内存管理更快(对于短期程序和/或大量内存).GC允许简单快速的分配,并且程序不会花时间解除分配.
C程序通常根据需要分配和释放内存.这是低效的.一个好的VM将在大块中分配和释放,在许多情况下会产生很大的性能提升.

2> Johannes Sch..:

C设计师已经做出了折衷.也就是说,他们决定将速度提高到安全之上.C不会

检查数组索引边界

检查未初始化的变量值

检查内存泄漏

检查空指针取消引用

当您索引到数组时,在Java中,它需要在虚拟机中进行一些方法调用,绑定检查和其他健全性检查.这是有效且绝对正常的,因为它增加了应有的安全性.但在C语言中,即使是非常琐碎的事情也不会安全.例如,C不需要memcpy来检查要复制的区域是否重叠.它不是设计为编写大型业务应用程序的语言.

但是这些设计决策并不是C语言中的错误.它们是设计的,因为它允许编译器和库编写者从计算机中获得所有性能.以下是C的精神C Rationale文档如何解释它:

C代码可以是不可移植的.虽然它努力为程序员提供编写真正可移植程序的机会,但委员会并不想强迫程序员编写程序,以排除使用C作为"高级汇编程序":编写特定于机器的能力代码是C的优势之一.

保持C的精神.委员会将保持C的传统精神作为一个主要目标.C的精神有许多方面,但其实质是C语言所依据的基本原则的社区情感.C精神的某些方面可以用短语来概括

相信程序员.

不要阻止程序员做需要做的事情.

保持语言小而简单.

只提供一种方法来进行操作.

即使不保证便携,也要快速.

最后一句谚语需要一点解释.高效代码生成的潜力是C最重要的优势之一.为了帮助确保看起来非常简单的操作不会发生代码爆炸,许多操作被定义为目标机器的硬件如何做而不是通过一般的抽象规则.在管理用于表达式的char对象的扩展的规则中可以看到这种意愿与机器一起工作的一个例子:char对象的值扩展到有符号或无符号量通常取决于哪个字节操作更多在目标机器上高效.


C通过暴力崩溃检查空指针deref :-).它还偶尔会通过搞砸堆栈帧和数据来检查超出范围的数组索引和未初始化的变量.不幸的是它在运行时检查这些.
我不会说C不安全,这听起来像你暗示的那样.它假设你不是白痴.如果你指着一把枪并用脚射击自己,C会高兴地让你这么做,因为它会让你觉得你比它更聪明.这不一定是坏事.
@Bob:没错.说C不安全,因为它让你做危险的事情就像说汽车不安全,因为它会让你开出悬崖.C和开车的人一样安全(但那里有很多不安全的司机).
鲍勃,制造缓冲区溢出等错误并不意味着你是个白痴.这只意味着你仍然是人类.我意识到C和C++并不坏*(我非常喜欢它们).
在我看来,这应该是公认的答案.
@ JohannesSchaub-litb C非常适合大规模应用程序编程.如果发现很难让项目比hello世界更大,那么问题在于程序员,而不是语言......
"它不是设计用于编写大型商业应用程序的语言." - 嗯?BS,充其量.你会用C编写的所有伟大的业务应用程序叫什么?
C和C++非常不安全.因此,Mozilla创建了一种全新的语言(Rust),以便成为更安全的替代品.使用C或C++处理可能存在恶意输入的任何非平凡可验证程序是安全漏洞的主要原因之一.人们犯了太多错误,"相信程序员"是一个很好的答案.

3> JosephStyons..:

如果你用一个月的时间在C中构建一个运行时间为0.05秒的东西,并且我花了一天时间在Java中编写相同的东西,并且它在0.10秒内运行,那么C真的更快吗?

但是要回答你的问题,编写良好的 C代码通常比其他语言中编写良好的代码运行得更快,因为编写C代码"井"的部分包括在近机器级别进行手动优化.

虽然编译器确实非常聪明,但他们还不能创造性地提出与手动按摩算法竞争的代码(假设"手"属于优秀的 C程序员).

编辑:

很多评论都是"我用C编写,我不考虑优化".

但是从这篇文章中得到一个具体的例子:

在Delphi中我可以这样写:

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;

在CI写这个:

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整数数组的指针,我们将其称为chars.在C中,我们一次测试一个角色存在.在Delphi中,我使用Pos.

这只是一个小例子.在一个大型程序中,C程序员必须使用每几行代码进行这些类型的低级决策.它增加了手工制作的手动优化可执行文件.


为了公平起见,在C中花费一个月的时间并不多,在Java中只需要一天只执行0.05秒(即小程序).
你说的是一个用C语言花了一个月的程序,它的速度是用Java编写的程序的两倍,只需要一天的时间就不值得吗?如果该计划每天需要运行500,000,000次以上怎么办?速度快两倍是非常重要的.如果它运行在数千或数百万个CPU上,那么额外一个月的开发成本将节省两倍的成本将是巨大的.基本上,在选择开发平台之前,您必须了解/了解部署的规模.
我已经在C中编程了多年,几乎从来没有做过任何你提到的优化.我已将一些程序移植到C(主要来自Perl),并且通常看到速度提高了10倍以上,并且在没有任何手动编码优化的情况下显着降低了内存使用量.
我创建了C++程序,可以在更短的时间内处理数千行数据,然后Java或.NET程序就可以启动了.这是我对更现代语言的挫折之一.C非常适合需要最小运行时要求的精益程序.PowerBasic也很棒.

4> Rob Williams..:

我没有看到它,所以我会说: C往往更快,因为几乎所有其他东西都是用C语言编写的.

Java是基于C构建的,Python是基于C(或Java,或.NET等)构建的,Perl是等等.操作系统是用C语言编写的,虚拟机是用C语言编写的,编译器是用C语言编写的,解释器是用C语言编写的.有些东西仍然用汇编语言编写,后者往往更快.越来越多的东西被写在其他东西上,这本身就是用C语写的.

您使用其他语言(而不是Assembly)编写的每个语句通常在C下面作为几个语句实现,这些语句被编译为本机机器代码.由于为了获得比C更高的抽象级别而倾向于存在其他语言,因此C中所需的那些额外语句往往侧重于增加安全性,增加复杂性和提供错误处理.这些往往是好事,但它们有成本,其名称是速度大小.

就个人而言,我已经用几十种语言编写了大部分可用频谱,我个人已经找到了你暗示的魔力:

我怎么能吃蛋糕呢?我怎样才能用我最喜欢的语言进行高级抽象,然后深入了解C的细节以获得速度?

经过几年的研究,我的答案是Python(在C上).你可能想看一看.顺便说一句,您也可以从Python下载到Assembly(在特殊库的帮助下).

另一方面,坏代码可以用任何语言编写.因此,C(或汇编)代码不会自动更快.同样,一些优化技巧可以使部分高级语言代码接近原始C的性能级别.但是,对于大多数应用程序,您的程序大部分时间都在等待人员或硬件,因此差异无关紧要.

请享用.


这并不适用于JIT编译的语言.这并不是说我的C#被编译成IL,它被翻译成C,编译成机器代码.不,IL是JIT编译的 - 并且JIT的实现语言在那时是无关紧要的.它只是生产机器代码.
上帝禁止我对传奇的Jon Skeet提出质疑,但是生产的机器代码似乎与C#而不是C相关,所以它是"更高级别",具有更多功能,具有安全检查等等.因此,比"等价"C慢.
@Jon:我正要说类似的东西,但实际上这点有点有效,因为很多.NET库核心组件实际上都是用C编写的,因此有C的速度限制.看看未来这将如何变化将会很有趣.
这个答案不正确.如上所述,它不适用于JIT语言,但它也不适用于具有自己的编译器的语言(如果付出非凡的努力,可以生成比现代C编译器更快的代码).剩下的唯一一类语言是解释语言,而且它们并不比C*慢*,因为它们本身是用C语言编写的,但是因为解释的开销,无论你如何切片,甚至是解释器都是在集会中,是巨大的.

5> Rob Allen..:

那里有很多问题 - 主要是我没有资格回答的问题.但对于最后一个:

是什么阻止其他语言能够编译成与C一样快的每一位运行的二进制文件?

总之,抽象.

C只是机器语言的一个或两个抽象层次.Java和.Net语言至少有3个抽象级别,远离汇编程序.我不确定Python和Ruby.

通常情况下,程序员玩具越多(复杂的数据类型等),您就越远离机器语言,必须完成更多的翻译工作.

我不在这里,但这是基本的要点.

更新 -------这篇文章有一些很好的评论,有更多细节.


@Robert:.net*会*使用VM..net代码被编译成由VM执行的字节码.VM在运行时将字节码转换为本机指令.
从技术上讲,Java和.Net是从他们运行的机器的机器语言中无限抽象出来的.它们在VM中运行.即使使用JIT,也必须对原始代码进行大规模按摩以获得类似于本机代码的内容.
值得注意的是,Java和其他OO语言抽象已经影响了处理器指令集.如果java VM了解这些优化并使用它们,则较新的处理器具有使Java运行更快的指令.它并不大,但它很有帮助.

6> Norman Ramse..:

由于C的成本模型是透明的,因此C的速度并不快.如果C程序很慢,那么它显然很慢:通过执行大量语句.与C中的操作成本相比,对对象(尤其是反射)或字符串的高级操作可能会产生不明显的成本.

通常编译为与C一样快的二进制文件的两种语言是标准ML(使用MLton编译器)和Objective Caml.如果你查看基准测试游戏,你会发现对于某些基准测试,比如二叉树,OCaml版本比C更快.(我没有找到任何MLton条目.)但是不要太认真地对待枪战; 正如它所说的那样,游戏结果通常反映了人们在调整代码方面投入了多少精力.



7> Tim Williscr..:

C并不总是更快.

C比现代Fortran慢.

对于某些事情,C通常比Java慢.(特别是在JIT编译器完成了代码之后)

C允许指针别名发生,这意味着一些好的优化是不可能的.特别是当您有多个执行单元时,这会导致数据获取停顿.噢.

假设指针算法确实能够在一些CPU系列上引起缓慢的性能(特别是PIC!)它曾经在分段x86上吮吸大的.

基本上,当你得到一个向量单元或并行编译器时,C会发臭,现代Fortran运行得更快.

C程序员的技巧就像thunking(动态修改可执行文件)导致CPU预取停顿.

你得到漂移?

我们的好朋友x86执行一套指令集,这些指令集与实际的CPU架构几乎没有关系.影子寄存器,加载存储优化器,都在CPU中.所以C接近虚拟金属.真正的金属,英特尔不会让你看到.(历史上VLIW CPU有点像一个半身像,所以也许没有那么糟糕.)

如果你在高性能DSP(可能是TI DSP?)上用C编程,编译器必须做一些棘手的工作来在多个并行执行单元中展开C. 所以在这种情况下C不接近金属,但它接近编译器,这将完成整个程序.奇怪的.

最后,一些CPU(www.ajile.com)在硬件中运行Java字节码.C将在PITA上使用该CPU.


你的大部分帖子都忽略了C99的存在.此外,许多C/C++编译器提供C99 restrict关键字(确保没有指针别名)作为扩展.
当c比现代Fortenberry慢时,您能举个例子吗?

8> aku..:

是什么阻止其他语言能够编译成与C一样快的每一位运行的二进制文件?

没有.Java或.NET等现代语言更多地依赖于程序员的生产力而不是性能.硬件现在很便宜.编译到中间表示也提供了许多奖励,例如安全性,可移植性等..NET CLR可以利用不同的硬件 - 例如,您不需要手动优化/重新编译程序以使用SSE指令集.



9> Matthew Crum..:

主要因素是它是一种静态类型的语言,并编译为机器代码.此外,由于它是一种低级语言,它通常不会做任何你不告诉它的事情.

这些是我想到的其他一些因素.

变量不会自动初始化

没有边界检查数组

未经检查的指针操作

没有整数溢出检查

静态类型的变量

函数调用是静态的(除非你使用函数指针)

编译器编写者有很多时间来改进优化代码.此外,人们在C中编程以获得最佳性能,因此优化代码的压力很大.

语言规范的一部分是实现定义的,因此编译器可以以最佳方式自由地执行操作

大多数静态类型的语言可以像C一样快速或快速地编译,特别是如果他们可以做出假设C不能因为指针别名等等.


C是一种低级语言.C一直是低级语言.您可以毫不费力地将C代码手动转换为汇编程序.
@Robert:C过去被认为是高级语言,因为与汇编语言相比(这是很常见的)。与当今使用的大多数语言相比,它被认为是一种低级语言。

10> PolyThinker..:

我猜你忘了汇编语言也是一种语言:)

但严重的是,只有当程序员知道他在做什么时,C程序才会更快.您可以轻松编写一个C程序,该程序运行速度比使用其他语言执行相同工作的程序慢.

C更快的原因是因为它是以这种方式设计的.它可以让你做很多"低级"的东西,帮助编译器优化代码.或者,我们应该说,程序员负责优化代码.但它通常非常棘手且容易出错.

与其他语言一样,其他语言更多地关注程序员的工作效率.人们普遍认为程序员的时间比机器时间要贵得多(即使在过去).因此,最大限度地减少程序员花在编写和调试程序上的时间而不是程序的运行时间是很有意义的.要做到这一点,你将牺牲一些你可以做的事情来使程序更快,因为许多事情是自动化的.


虽然如果你用C语言编写一次程序并再次在Assembly中编写,那么C版本可能会更快,因为编译器比你更聪明.

11> Peter Lawrey..:

C++的平均速度更快(因为它最初是C的超级集合).但是对于特定的基准测试,通常会有另一种语言更快.

https://benchmarksgame-team.pages.debian.net/benchmarksgame/

fannjuch-redux 在Scala中速度最快

n-bodyfasta分别在阿达更快.

spectral-norm 在Fortran中最快.

reverse-complement,mandelbrot并且pidigits是最快的ATS.

regex-dna 是最快的JavaScript.

chameneou-redux 最快的是Java 7.

thread-ring 在Haskell最快.

其余的基准测试在C或C++中最快.


这就像说`system("bash script.sh");`适用于任何bash脚本,因此C是bash的超集.`extern"C"`由于名称重整而在C++中提供C链接.将X作为Y的超集调用意味着可以在Y中完成的任何操作也可以在X中完成,而C++则不然.有很多语言结构在C语言中有效,但在C++中却没有.

12> Daemin..:

我知道很多人都说了很长的话,但是:

C更快,因为它为您做的更少。



13> P Daddy..:

这些答案中的许多答案给出了为什么C更快或更快(在一般情况下或在特定情况下)的正当理由.不可否认的是:

许多其他语言提供我们认为理所当然的自动功能.例如,边界检查,运行时类型检查和自动内存管理不是免费的.至少有一些与这些功能相关的成本,在编写使用这些功能的代码时,我们可能没有想到 - 甚至没有意识到.

从源到机器的步骤在其他语言中通常不像在C中那样直接.

OTOH,要说编译的C代码比其他语言编写的其他代码执行得更快,这种概括并不总是正确的.反例很容易找到(或设法).

尽管如此,我认为还有一些事情,我认为,与其他许多因素相比,C与其他语言的比较性能更为显着.以机智:

其他语言通常可以更容易地编写执行速度更慢的代码.通常,它甚至受到语言设计理念的鼓舞.推论:C程序员更有可能编写不执行不必要操作的代码.

例如,考虑一个简单的Windows程序,其中创建一个主窗口.AC版本将填充WNDCLASS[EX]将传递给的结构RegisterClass[Ex],然后调用CreateWindow[Ex]并进入消息循环.高度简化和缩写的代码如下:

WNDCLASS wc;
MSG      msg;

wc.style         = 0;
wc.lpfnWndProc   = &WndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = hInstance;
wc.hIcon         = NULL;
wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName  = NULL;
wc.lpszClassName = "MainWndCls";

RegisterClass(&wc);

CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
             CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

while(GetMessage(&msg, NULL, 0, 0)){
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

C#中的等效程序可能只是一行代码:

Application.Run(new Form());

这一行代码提供了近20行C代码所做的所有功能,并添加了一些我们遗漏的东西,例如错误检查.更丰富,更完整的库(与典型的C项目中使用的库相比)为我们做了很多工作,让我们有时间编写更多代码片段,这些代码片段看起来很短,但涉及幕后的许多步骤.

但是,一个丰富的库可以实现简单快速的代码膨胀,这不是我的观点.当你开始检查当我们的小单行实际执行时实际发生了什么时,我的观点会更加明显.为了好玩,在Visual Studio 2008或更高版本中启用.NET源代码访问,并进入上面的简单one-linef.你会遇到的一个有趣的小宝石是getter中的这个评论Control.CreateParams:

// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
// 
if (createParams == null) {
    createParams = new CreateParams(); 
} 

十次.大致相当于什么是存储在一个总和的信息WNDCLASSEX结构,什么是传递到CreateWindowEx从检索Control十倍类之前它存储在WNDCLASSEX结构上传递RegisterClassExCreateWindowEx.

总而言之,执行此基本任务所执行的指令数量在C#中比在C中多出2-3个数量级.部分原因是由于使用了功能丰富的库,这必然是一般化的,而不是我们的简单C代码完全符合我们的需求,仅此而已.但其中一部分原因在于.NET框架的模块化,面向对象的特性使得自身经常会通过程序方法避免重复执行.

我不是试图选择C#或.NET框架.我也不是说模块化,泛化,库/语言特性,OOP等都是坏事.我曾经在C中进行大部分开发,后来在C++中进行,最近在C#中进行.同样,在C之前,我主要使用汇编.随着语言的每一步"更高",我会在更短的时间内编写更好,更易维护,更强大的程序.但是,它们的执行速度往往会慢一些.


这是一个API问题,而不是语言问题.

14> 小智..:

我认为没有人提到过C编译器比其他任何编译器都付出更多努力的事实,可能除了Java之外.

由于许多原因,C极其优化 - 比几乎任何其他语言都要多.因此,如果将相同的努力量投入到其他语言编译器中,C可能仍会名列前茅.

我认为至少有一种候选语言可以比C更好地优化,因此我们可以看到生成更快二进制文件的实现.我正在考虑数字火星D,因为创建者注意构建一种可能比C更好地优化的语言.可能还有其他语言具有这种可能性.但是我无法想象任何语言都会让编译器比最好的C编译器快几个百分点.我想爱错.

我认为真正的"低悬的果实"将是用于人类优化的语言.熟练的程序员可以使任何语言更快 - 但有时你必须做荒谬的事情或使用不自然的结构来实现这一点.虽然它总是需要付出努力,但是一个好的语言应该能够产生相对快速的代码,而不必过于专注于程序的编写方式.

同样重要的是(至少对我而言)最坏情况的代码往往很快.网上有许多"证明",Java比C更快或更快,但这是基于樱桃挑选示例.我不是C的忠实粉丝,但我知道我用C写的任何东西都会运行良好.使用Java,它"可能"在15%的速度内运行,通常在25%以内,但在某些情况下可能会更糟.任何情况下,它的速度一样快或在几个百分之内,通常是由于大部分时间花在库代码上,无论如何都要进行大量优化.



15> AShelly..:

在大多数情况下,每个C指令对应于非常少的汇编指令.您实际上是在编写更高级别的机器代码,因此您几乎可以控制处理器所做的一切.许多其他编译语言,如C++,有很多简单易懂的指令,可以转化为比你想象的更多的代码(虚函数,复制构造函数等).而像Java或Ruby这样的解释语言有另外一层您从未见过的说明 - 虚拟机或解释器.



16> James..:

这实际上是一种长期存在的谎言.虽然C程序经常更快,但情况并非总是如此,特别是如果C程序员不是很擅长的话.

人们倾向于忘记的一个很大的漏洞是程序必须阻止某种IO,例如任何GUI程序中的用户输入.在这些情况下,使用何种语言并不重要,因为您受到数据进入速率的限制,而不是处理数据的速度.在这种情况下,如果您使用C,Java,C#甚至Perl,这并不重要; 你不能比数据进来更快.

另一个主要问题是使用垃圾收集而不使用正确的指针允许虚拟机进行许多其他语言不可用的优化.例如,JVM能够在堆上移动对象以对其进行碎片整理.这使得将来的分配更快,因为可以简单地使用下一个索引而不是在表中查找它.现代JVM也不必实际释放内存; 相反,他们只是移动活动对象,当他们GC和死对象的花费的内存基本上是免费恢复时.

这也引出了一个关于C的有趣观点,在C++中更是如此.有一种设计理念是"如果你不需要它,你就不需要付钱".问题是,如果你真的想要它,你最终会为此付出代价.例如,Java中的vtable实现往往比C++实现好很多,因此虚函数调用要快得多.另一方面,你别无选择,只能在Java中使用虚拟功能,但它们仍然需要花费一些成本,但在使用大量虚拟功能的程序中,降低的成本会增加.


@James - 争论"I/O使性能降低"不会使语句"C比其他语言更快"无效.这不是一个明显的漏洞,这是一个稻草人的论点.
推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有