在听StackOverflow播客的时候,经常有人说“真正的程序员”是用C语言编写的,而C语言的速度要快得多,因为它“接近机器”。把前面的断言留到另一篇文章,C有什么特别之处,使它比其他语言更快?或者换句话说:什么能阻止其他语言编译成二进制代码,使其运行速度与C语言一样快?
当前回答
我还没有看到它,所以我要说: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语言的性能水平。但是,对于大多数应用程序来说,程序的大部分时间都在等待人员或硬件,因此两者之间的差异实际上并不重要。
享受。
其他回答
这不是语言的问题,而是工具和库的问题。C语言可用的库和编译器比新语言要老得多。你可能认为这会让它们变慢,但事实恰恰相反。
这些库是在处理能力和内存非常重要的时候编写的。它们必须写得非常高效,才能发挥作用。C编译器的开发人员也花了很长时间为不同的处理器进行各种巧妙的优化。C语言的成熟和广泛采用使得它比同时期的其他语言具有显著的优势。它还使C语言在速度上比那些不像C语言那样强调原始性能的新工具更有优势。
C的设计者们已经做出了取舍。也就是说,他们决定把速度放在安全之上。C不会
检查数组下标边界 检查未初始化的变量值 检查内存泄漏 检查空指针解引用
当你索引到一个数组时,在Java中,它接受虚拟机中的一些方法调用,绑定检查和其他健全检查。这是有效的,绝对没问题,因为它在应有的地方增加了安全性。但是在C语言中,即使是非常微不足道的东西也不会被放在安全的地方。例如,C不要求memcpy检查要复制的区域是否重叠。它并不是一种用于编写大型商业应用程序的语言。
但是这些设计决策并不是C语言中的bug。它们是被设计出来的,因为它允许编译器和库编写者从计算机中获得每一点性能。下面是C语言的精神——C语言的基本原理文档是这样解释的:
C code can be non-portable. Although it strove to give programmers the opportunity to write truly portable programs, the Committee did not want to force programmers into writing portably, to preclude the use of C as a ``high-level assembler'': the ability to write machine-specific code is one of the strengths of C. Keep the spirit of C. The Committee kept as a major goal to preserve the traditional spirit of C. There are many facets of the spirit of C, but the essence is a community sentiment of the underlying principles upon which the C language is based. Some of the facets of the spirit of C can be summarized in phrases like Trust the programmer. Don't prevent the programmer from doing what needs to be done. Keep the language small and simple. Provide only one way to do an operation. Make it fast, even if it is not guaranteed to be portable. The last proverb needs a little explanation. The potential for efficient code generation is one of the most important strengths of C. To help ensure that no code explosion occurs for what appears to be a very simple operation, many operations are defined to be how the target machine's hardware does it rather than by a general abstract rule. An example of this willingness to live with what the machine does can be seen in the rules that govern the widening of char objects for use in expressions: whether the values of char objects widen to signed or unsigned quantities typically depends on which byte operation is more efficient on the target machine.
这些答案中的许多都给出了为什么C更快或更快的有效理由(无论是在一般情况下还是在特定的场景中)。不可否认的是:
Many other languages provide automatic features that we take for granted. Bounds checking, run-time type checking, and automatic memory management, for example, don't come for free. There is at least some cost associated with these features, which we may not think about—or even realize—while writing code that uses these features. The step from source to machine is often not as direct in other languages as it is in C. OTOH, to say that compiled C code executes faster than other code written in other languages is a generalization that isn't always true. Counter-examples are easy to find (or contrive).
尽管如此,我还是注意到另一件事,我认为它比其他任何因素都更能影响C与许多其他语言的比较性能。即:
其他语言通常更容易编写执行较慢的代码。通常,它甚至受到该语言的设计哲学的鼓励。推论:C程序员更有可能编写不执行不必要操作的代码。
例如,考虑一个简单的Windows程序,其中创建了一个主窗口。C版本将填充一个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源代码访问,然后进入上面简单的一行代码。您将遇到的一个有趣的小珍宝是Control的getter中的这个注释。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的内容的总和,在它存储在WNDCLASSEX结构中并传递给RegisterClassEx和CreateWindowEx之前,从Control类中检索10次。
总而言之,在c#中执行这个非常基本的任务的指令数量比在C中多2-3个数量级,部分原因是使用了功能丰富的库,这是必然的,而我们简单的C代码只做了我们需要的事情,仅此而已。但部分原因是. net框架的模块化、面向对象的特性导致了大量的重复执行,而过程方法通常可以避免这些重复执行。
我并不是要挑c#或。net框架。我也不是说模块化、泛化、库/语言特性、OOP等等都是不好的东西。我曾经用C语言进行大部分开发,后来用c++,最近用c#。类似地,在使用C之前,我主要使用汇编。我的语言每“提高”一步,我就能在更短的时间内写出更好、更可维护、更健壮的程序。然而,它们的执行速度往往要慢一些。
我知道很多人都说过这句话,但是:
C更快,因为它(为你)做的更少。
撇开诸如热点优化、预编译元算法和各种形式的并行等高级优化技术不提,语言的基本速度与支持通常在内部循环中指定的操作所需的隐含的幕后复杂性密切相关。
也许最明显的方法是对间接内存引用进行有效性检查——比如检查指针是否为空,检查索引是否符合数组边界。大多数高级语言隐式地执行这些检查,但C不这样做。然而,这并不一定是这些其他语言的基本限制——一个足够聪明的编译器可能能够通过某种形式的循环不变代码运动,从算法的内部循环中删除这些检查。
C语言(在类似程度上与c++密切相关)更基本的优势是严重依赖基于堆栈的内存分配,这本质上是快速的分配、回收和访问。在C(和c++)中,主调用堆栈可用于分配原语、数组和聚合(结构/类)。
虽然C语言确实提供了动态分配任意大小和生命周期的内存的能力(使用所谓的“堆”),但默认情况下是避免这样做的(而是使用堆栈)。
诱人的是,有时可以在其他编程语言的运行时环境中复制C内存分配策略。asm.js已经证明了这一点,它允许用C或c++编写的代码被翻译成JavaScript的子集,并以接近本机的速度安全地运行在web浏览器环境中。
As somewhat of an aside, another area where C and C++ outshine most other languages for speed is the ability to seamlessly integrate with native machine instruction sets. A notable example of this is the (compiler and platform dependent) availability of SIMD intrinsics which support the construction of custom algorithms that take advantage of the now nearly ubiquitous parallel processing hardware -- while still utilizing the data allocation abstractions provided by the language (lower-level register allocation is managed by the compiler).
推荐文章
- 为什么C语言这么快,为什么其他语言没有这么快或更快?
- 转发C中可变函数的调用
- String与StringBuilder
- 当尝试用const初始化变量时,错误“初始化元素不是常量”
- 为什么数组的地址等于它在C语言中的值?
- 为什么引入无用的MOV指令会加速x86_64汇编中的紧循环?
- C语言中空指针的指针算术
- 确定导致分段错误的代码行?
- 如何在SQL中有效地计数列值的发生?
- 使用curl在PHP中获取HTTP代码
- 确定PHP中是否存在数组键的更快更好的方法是什么?
- 如何在O(n)中找到长度为n的无序数组中的第k大元素?
- postgresql COUNT(DISTINCT…)非常慢
- 我可以在C或c++中使用二进制文字吗?
- fork和exec的区别