首页 >后端开发 >C++ >C和C真的那么快吗?

C和C真的那么快吗?

Susan Sarandon
Susan Sarandon原创
2024-12-07 09:25:13951浏览

C and C   are really so fast?

我从事编程的那段时间,听说C和C是速度标准。最快中的最快,直接编译为汇编代码,速度上没有什么可以与 C 或 C 竞争。而且,似乎没有人挑战这个共同信念。

计算性能

显然,数字算术运算在 C 中的运行速度必须比在任何其他语言中快得多。但他们有吗?
前段时间,我决定为许多不同的语言编写一组简单的基准测试,看看速度到底有多大差异。
想法很简单:从零开始,使用直接计算来找到十亿个整数的总和。一些编译器(例如rustc)用公式表达式替换这种简单的循环,当然,公式表达式将在恒定的时间内进行计算。用这样的编译器来避免这种情况。我在数字成本运算中使用了类似的方法,例如按位或
得到结果后,我非常惊讶。我的世界观发生了翻天覆地的变化,我不得不重新考虑我所知道的关于编程语言速度的一切。
您可以在下表中看到我的结果:

Linux 64 位1.1 GHz CPU,4GB RAM

语言 编译器/版本/参数 时间 标题> Rust(
Language compiler/version/args time
Rust (bitwise or instead of ) rustc 1.75.0 with -O3 167 ms
C gcc 11.4.0 with -O3 335 ms
NASM 2.15.05 339 ms
Go 1.18.1 340 ms
Java 17.0.13 345 ms
Common Lisp SBCL 2.1.11 1 sec
Python 3 pypy 3.8.13 1.6 sec
Clojure 1.10.2 9 sec
Python 3 cpython 3.10.12 26 sec
Ruby 3.0.2p107 38 sec
按位或而不是 ) 带有 -O3 的 rustc 1.75.0 167 毫秒 C 带有-O3的gcc 11.4.0 335 毫秒 NASM 05.15 339 毫秒 去 1.18.1 340 毫秒 Java 17.0.13 345 毫秒 通用 Lisp SBCL 2.1.11 1秒 Python 3 pypy 3.8.13 1.6 秒 Clojure 1.10.2 9秒 Python 3 cpython 3.10.12 26 秒 红宝石 3.0.2p107 38秒 表>

您可以在这里找到所有测试来源:
https://github.com/Taqmuraz/speed-table

因此,正如我们所看到的,C 并不比 Java 快很多,差异约为 3%。此外,我们还发现其他编译语言的算术运算性能与 C 非常接近(Rust 甚至更快)。使用 JIT 编译器 编译的动态语言显示出更糟糕的结果 - 主要是因为算术运算被包装到动态分派的函数中。
解释型动态语言没有 JIT 编译器表现出最差的性能,这并不奇怪。

内存分配性能

在那次惨败之后,C 粉丝会说 C 中的内存分配要快得多,因为你直接从系统分配它,而不是要求 GC
现在和之后我将使用 GC 术语作为 垃圾收集器托管堆,具体取决于上下文。
那么,为什么人们认为 GC 这么慢?事实上,GC已经预先分配了内存,分配就是简单地将指针向右移动。大多数情况下 GC 使用系统调用将分配的内存填充为零,类似于 C 中的 memset,因此需要 恒定时间。而 C 中的内存分配需要不确定的时间,因为它取决于系统和已经分配的内存。
但是,即使考虑到这些知识,我也无法期望 Java 能取得如此好的结果,您可以在下表中看到:

标题>
1.1 GHz 2 cores, 4 GB RAM
Running tests on single thread.
Result format : "Xms-Yms ~Z ms" means tests took from X to Y milliseconds, and Z milliseconds in average
1.1 GHz 2 核,4 GB RAM 在单线程上运行测试。 结果格式:“Xms-Yms ~Z ms”表示测试从X到Y毫秒,平均Z毫秒 表>

分配整数数组

integers array size times Java 17.0.13 new[] C gcc 11.4.0 malloc Common Lisp SBCL 2.1.11 make-array
16 10000 0-1ms, ~0.9ms 1-2ms, ~1.2ms 0-4ms, ~0.73ms
32 10000 1-3ms, ~1.7ms 1-3ms, ~1.7ms 0-8ms, ~2.ms
1024 10000 6-26ms, ~12ms 21-46ms, ~26ms 12-40ms, ~7ms
2048 10000 9-53ms, ~22ms 24-52ms, ~28ms 12-40ms, ~19ms
16 100000 0-9ms, ~2ms 6-23ms, ~9ms 4-24ms, ~7ms
32 100000 0-14ms, ~3ms 10-15ms, ~11ms 3-8ms, ~7ms
1024 100000 0-113ms, ~16ms 234-1156ms, ~654ms 147-183ms, ~155ms
2048 100000 0-223ms, ~26ms 216-1376ms, ~568ms 299-339ms, ~307ms

为 Person 类的实例分配一个整型字段。

how many instances Java 17.0.3 new Person(n) C g 11.4.0 new Person(n)
100000 0-6ms, ~1.3ms 4-8ms, ~5ms
1 million 0-11ms, ~2ms 43-69ms, ~47ms
1 billion 22-50ms, ~28ms process terminated

您可以在这里找到所有测试来源:
https://github.com/Taqmuraz/alloc-table

在那里我总共测试了四种语言:C、C、Java 和 Lisp。而且,带有 GC 的语言总是显示出更好的结果,尽管我对它们的测试比 C 和 C 严格得多。例如,在 Java 中,我通过虚拟函数调用分配内存,因此它可能不会被静态优化,而在 Lisp 中,我正在检查分配数组的第一个元素,因此编译器不会跳过分配调用。

释放内存

C 粉丝仍然有动力去保护他们的信仰,所以,他们说“是的,你分配内存确实更快,但你必须在之后释放它!”。
真的。而且,突然之间,GC 释放内存的速度比 C 还要快。但是如何呢?想象一下,我们从 GC 进行了 100 万次分配,但后来我们的程序中只引用了 1000 个对象。而且,比方说,这些对象分布在所有这么长的内存范围内。 GC 进行堆栈跟踪,找到那 1000 个“活动”对象,将它们移动到上一代堆峰值,并将堆峰值指针放在最后一个对象之后。就这些了。 所以,
无论你分配了多少对象GC的工作时间取决于之后你保留了多少对象 而且,与此相反,在 C 中,您必须手动释放所有分配的内存,因此,如果您分配内存 100 万次,您也必须进行 100 万次释放调用(否则将会出现内存泄漏)。这意味着
GCO(1)-O(n) 与 C 的 O(n) 或更差,其中 n是之前发生的分配次数。

概括

所以,我想巩固垃圾收集语言对 C 和 C 的胜利。这是总结表:

要求 带有 GC 的语言 C/C 标题> 算术 使用
JIT
demands languages with GC C/C
arithmetic fast with JIT fast
allocating memory fast O(1) slow
releasing memory fast O(1) best case, O(n) worst case O(n) or slower
memory safe yes no
快速
快 分配内存 快O(1) 慢 释放内存 快速O(1)最好情况,O(n)最坏情况 O(n) 或更慢 内存安全 是 没有 表>

现在我们可能会看到——垃圾收集并不是一种必要的罪恶,而是我们唯一希望拥有的最好的东西。它为我们提供了安全性能两者

向C致敬

虽然 C 在我的测试中确实显示出较差的结果,但它仍然是一门重要的语言,并且有自己的应用领域。我的文章并不是为了拒绝或删除 C。 C也不错,只是没有人们想象的那么优越。许多好的项目失败只是因为有些人决定使用 C 而不是 Java,例如,因为他们被告知 C 更快,而 Java 由于垃圾收集而慢得令人难以置信。当我们编写非常小且简单的程序时,C 很好。但是,我绝不会建议用 C 编写复杂的程序或游戏。

C 不同

C 不简单,不灵活,语法超载,规范太多复杂。使用 C 编程,你不会实现自己的想法,但 90% 的时间都会与编译器和内存错误作斗争。
这篇文章的目的是拒绝C语言,因为速度和性能只是人们在软件开发中使用这种语言的借口。使用 C ,您要付出时间、程序性能和心理健康的代价。所以,当你在 C 和任何其他语言之间做出选择时,我希望你选择最后一种。

以上是C和C真的那么快吗?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn