C#比C++慢吗?

85

我已经思考这个问题有一段时间了。

当然,在C#中有些东西并没有为速度做出优化,所以使用那些对象或语言调整(例如LinQ)可能会导致代码变慢。

但是,如果您不使用任何这些调整,而只是将同样的代码片段在C#和C++中进行比较(很容易将一个翻译为另一个),它真的会慢那么多吗?

我看到的比较表明,在某些情况下,C#可能会更快,因为理论上JIT编译器应该实时优化代码并获得更好的结果:

托管还是非托管?

我们应该记住,JIT编译器在实时编译代码,但这是一次性的开销,相同的代码(一旦到达和编译)不需要再次在运行时编译。

GC也不会增加太多额外的开销,除非您创建和销毁成千上万的对象(如使用String而不是StringBuilder)。在C++中这样做也将很昂贵。

我想提出的另一点是,在.NET中引入的DLL之间更好的通信方式。.Net平台比基于托管COM的DLL更好地进行通信。

我没有看到任何固有的原因说明为什么语言应该更慢,而且我真的认为C#不比C++慢(从经验和缺乏一个好的解释来看)。

那么,用C#编写的相同代码片段是否会比C++中的代码更慢? 如果是,那么为什么?

一些其他参考资料(虽然谈论了这个问题,但没有关于为什么的解释):

如果C++比C++慢,为什么要使用C#?


3
真的...这取决于...有些事情更快,有些事情更慢。C/C++更具“确定性”(没有垃圾收集器)。如果你想要创建100个线程,我可以告诉你,GC会以其缓慢的速度来困扰你(并且在告诉我100个线程太多之前,请知道Skype和McAfee AV现在在我的电脑上每个都有40个线程)...在C#中编组是很麻烦的(而且速度较慢)。编码相当快。不,这不是挑衅。我真正更喜欢C#。 - xanatos
1
我认为这个问题应该被编辑并重新开放。我认为提问者可能不理解一个更基本的问题影响了C#和像C或C++这样的语言之间的性能差异,即“由C编译器生成的代码(以及它如何执行)与由C#编译器生成的代码之间有什么区别?” C#/Java和其他解释型或VM语言在运行其字节码时可能有几个中间步骤,在等效的C程序中完全不存在。 - Jonathon Faust
3
虽然您可以争辩说(如所述),这符合“不是一个真正的问题”的定义,但声称这是主观的是没有意义的。 “A是否比B更快”是一件可以开放客观测量的事情。 即使通过这样的测量得到的答案是含糊的(有些东西在一个方面更快,而在另一个方面更快),它仍然是客观的。 - Jerry Coffin
1
请参阅计算机语言基准测试,对许多不同类型的问题进行全面比较。 - Assaf Lavie
使用C++并不能自动产生更快的程序。C++可以让你更好地控制程序的性能特征,从而使你能够编写更快的程序,但这需要付出很多努力。 - Ferruccio
显示剩余6条评论
12个回答

160

警告:您提出的问题实际上非常复杂,可能比您意识到的要复杂得多。因此,这个答案会很长。

从纯理论角度来看,对于这个问题,可能存在一个简单的答案:没有什么是C#本质上无法像C++一样快的。然而,尽管理论上如此,但有些实际原因使得在某些情况下它依然比C++慢。

我将考虑三个基本区别:语言特性、虚拟机执行和垃圾回收。后两者经常一起使用,但可以独立,因此我将分别考虑它们。

语言特性

C++非常重视模板,并且模板系统中的功能主要旨在允许尽可能多的工作在编译时完成,因此从程序的角度来看,它们是“静态的”。模板元编程允许在编译时进行完全任意的计算(即,模板系统是图灵完备的)。因此,几乎所有不依赖于用户输入的内容都可以在编译时计算出来,因此在运行时只是一个常数。但是,这里的输入可能包括类型信息,所以你通常在C++中通过模板元编程在编译时完成的大量反射操作,在C#中通常是在运行时完成的。但是,运行时速度和灵活性之间存在明显的权衡 - 模板系统可以静态地做到的事情,但它们无法做到反射所能做到的一切。

语言特性上的差异意味着几乎任何试图通过将一些C#代码直接转换成C++(或反之亦然)来比较两种语言的尝试都可能产生无意义或误导的结果(对于大多数其他语言对也同样适用)。简单的事实是,对于任何超过几行代码的内容,几乎没有人会以与这种比较相同(或接近相同)的方式使用这些语言,因此该比较无法告诉您这些语言在实际应用中的工作方式。

虚拟机

像几乎所有现代VM一样,微软的.NET VM可以并且将会进行JIT(即“动态”)编译。这表示存在一些权衡。

主要来说,优化代码(像大多数其他优化问题一样)在很大程度上是一个NP完全问题。除非是一个真正微不足道/玩具式的程序,否则你几乎无法真正“优化”结果(即无法找到真正的最优解)——优化器只会使代码比之前更好。然而,许多众所周知的优化技术需要大量时间(而且通常还需要内存)才能执行。使用JIT编译器时,用户需要等待编译器运行。大多数较昂贵的优化技术都被排除在外。静态编译有两个优点:首先,如果它很慢(例如,构建一个大型系统),通常在服务器上进行,而且没有人需要等待它。其次,可以生成一个可执行文件,并由许多人重复使用。第一个最小化了优化成本;第二个则通过更多次的执行摊销了更小的成本。

如原问题所述(以及许多其他网站),JIT编译确实有可能更加了解目标环境,这应该(至少理论上)抵消了这种优势。毫无疑问,这个因素可以抵消静态编译的至少一部分劣势。然而,在我测试和经验中,这种情况相当罕见。针对特定类型的代码和目标环境,目标相关优化大多数似乎只会产生相当小的差异,或者只能(至少自动地)应用于相当特定类型的问题。明显的情况是,如果你在现代机器上运行一个相对旧的程序。一个用C++编写的旧程序可能已经被编译为32位代码,并且即使在现代的64位处理器上也将继续使用32位代码。用C#编写的程序将被编译为字节码,然后VM将把它编译为64位机器代码。如果该程序从以64位代码形式运行中获得了实质性的好处,那么这可能会给出一个很大的优势。在64位处理器比较新时的短时间内,这种情况经常发生。最近的代码通常可以静态编译成64位代码。

使用虚拟机还有可能提高缓存使用率。虚拟机指令通常比本机指令更简洁。在给定的缓存内存量中可以放置更多的指令,因此任何给定的代码在需要时被缓存的机会更大。这可以帮助保持解释执行的VM代码比大多数人最初预期的更具竞争力(速度方面)——在现代CPU上,你可以在执行一个缓存未命中时执行许多条指令。

值得一提的是,这个因素在两者之间并不一定有所不同。没有任何阻碍(例如),使C++编译器产生输出以在虚拟机上运行(带有或不带有JIT)。事实上,微软的C++/CLI就几乎是这样--一个几乎符合规范的C++编译器(尽管具有许多扩展),它产生了旨在在虚拟机上运行的输出。
反之亦然:微软现在拥有.NET Native,将C#(或VB.NET)代码编译为本机可执行文件。这提供了与C++相似的性能,但保留了C#/VB的功能(例如,编译为本机代码的C#仍支持反射)。如果您有性能密集型的C#代码,这可能会有所帮助。
垃圾回收: 从我所看到的,我可以说垃圾回收是这三个因素中最难理解的。只举一个显而易见的例子,这里的问题提到:“除非您创建和销毁数千个对象,否则GC不会添加太多开销[...]”。事实上,如果您创建并销毁数千个对象,垃圾回收的开销通常会相当低。.NET使用分代清除器,这是一种复制收集器的变体。垃圾回收器从指针/引用“已知”可访问的“位置”(例如,寄存器和执行堆栈)开始工作。然后,它“追踪”这些指针到在堆上分配的对象。它检查这些对象是否有进一步的指针/引用,直到跟随它们到链的末端,并找到所有(至少可能)可访问的对象。在下一步中,它获取所有处于使用状态或至少“可能在使用”的对象,并通过将它们全部复制到正在管理的内存的末端的连续块中来压缩堆。其余的内存是自由的(模除必须运行finalizers,但至少在编写良好的代码中,它们很少到我会忽略它们为止)。
这意味着如果您创建并销毁大量对象,则垃圾回收几乎不会增加开销。垃圾回收周期所需的时间几乎完全取决于已创建但尚未销毁的对象数量。快速创建和销毁对象的主要后果仅仅是GC必须更频繁地运行,但每个周期仍将很快。如果您创建对象而没有销毁它们,GC将更频繁地运行,并且每个周期都会明显变慢,因为它花费更多的时间追踪指向可能存活对象的指针,并花费更多的时间复制仍在使用中的对象。为了应对这个问题,代际回收机制的基本假设是已经存在很长时间的对象将有可能在未来一段时间内继续“存活”。因此,该机制会让在多次垃圾回收后仍然存活的对象进入“老年代”,并且垃圾回收器开始简单地假定它们仍在使用中,而不是在每个回收周期都将它们复制一次。这种情况通常足够有效,可以显著降低代际回收机制相对于其他垃圾回收形式的开销。
“手动”内存管理同样经常被误解。例如,许多比较尝试假定所有手动内存管理都遵循一种特定模型(例如最佳适配分配)。这与许多人对垃圾回收的信念(例如广泛认为它通常是通过引用计数完成的)一样,往往与现实接近程度甚微,或者根本不接近。
考虑到垃圾回收和手动内存管理的各种策略,很难比较它们的整体速度。仅试图比较分配和/或释放内存的速度(单独)几乎肯定会产生毫无意义的结果,甚至是彻头彻尾的误导。
额外话题:基准测试
由于相当多的博客、网站、杂志文章等声称提供单向“客观”证据,因此我也会谈一下这个问题。
这些基准测试大多类似于青少年决定比赛他们的车,赢家能保留两辆车。但这些网站有一个关键的不同之处:发布基准测试的人能够驾驶两辆车。出奇的是,他的车总是获胜,而其他所有人都只能接受“信任我,我确实以最快的速度驾驶了你的车”的结果。
编写一个产生毫无意义结果的低质量基准测试非常容易。几乎任何具备设计可产生有意义结果基准测试的技能的人,也具备编写将给出他所期望结果的基准测试的技能。事实上,编写代码来产生特定结果可能比编写真正产生有意义结果的代码更容易。
正如我的朋友James Kanze所说:“永远不要相信你没有自己篡改过的基准测试。”
结论
并没有简单的答案。我相当确定我可以抛硬币选择获胜者,然后在1到20的百分比范围内选择一个数字,并编写一些看起来合理和公正的代码,产生预先设定的结果(至少在某些目标处理器上——不同处理器可能会使百分比略微变化)。

正如其他人所指出的,对于大多数代码而言,速度几乎不重要。与此相对应的是(这个往往被忽视),在那些速度确实很重要的代码中,它通常非常重要。至少在我的经验中,在那些真正需要速度的代码中,C++几乎总是取胜者。虽然有利于C#的因素肯定存在,但在实践中,它们似乎被有利于C ++的因素所压倒。当你编写真实的代码时,你肯定可以找到一些基准测试来表明你的选择结果,但你几乎总是可以让它在C++中比在C#中更快。这可能需要更多的技能和/或努力来编写,但几乎总是可行的。


6
在之前的公司,我们有些决策是基于对标准的比较而做出的,但是由于错失了更全面的视野(例如对公司整体业务的影响)或者做得不好,所以这些比较并没有起到很好的作用。要小心对标准进行比较…很容易根据脱离上下文的事情做出决策。如果你要比较,请尽可能使其具有代表性 :) - Mark Simpson
4
据我所知,.NET运行时从不解释代码,而总是进行即时编译(JIT)。编译器在编译时(源代码到IL)和Jitter在运行时(IL到本地指令)都会进行优化。如果一个方法的JIT时间太长,Jitter可能会选择不进行优化。如果需要完全优化,则可以使用NGen在安装时生成本机映像。 - Dudu
2
我认为答案其实很简单:我的回答是,“是的,因为C#几乎强制您将数据结构定义为引用类型,这会在每个包装层中添加一层新的间接性,增加内存流量并降低内存局部性。在C++中,无论您围绕对象放置多少层包装,该对象都将保持在内存中的相同位置。” 你同意吗? - user541686
2
@Mehrdad:是的和不是的。从实际角度来看,是的,这很可能更有利于C++。同时,GC可以使得在许多情况下轻松地传递引用以避免复制数据。 - Jerry Coffin
2
@Mehrdad:是的——我所说的就是这种情况:“你几乎总是可以用C++比C#更快。编写它可能需要更多的技巧和/或努力,但几乎总是有可能的。” - Jerry Coffin
显示剩余13条评论

43

因为你并不总是需要使用(我用这个词比较宽泛)“最快”的语言?我上班也不会开法拉利,只因为它跑得更快...


19
我认为这个比喻非常贴切。如果我开着一辆法拉利去上班,我不会更快到达目的地。车子本身能够比我驾驶的速度更快,但车子的性能并不是限制因素。有速度限制、交通、路况等等因素。同样的,软件通常受用户交互、输入输出等方面的限制。在这种情况下,所使用的编程语言不会有太大区别。 - Fred Larson
3
好的,但问题是“哪辆车是法拉利,哪辆是大众?”或者两辆车是否都是法拉利。 - pepr
@pepr 标题中的问题是肯定的,但帖子末尾的问题是“为什么要使用大众汽车,而不是法拉利?” - Adam Houldsworth
@AdamHouldsworth: 是的。答案可以类似--“成本很重要”。 - pepr
@CashCow是的,因为你的摩托车可以穿梭于交通之间,这是这种情况下的正确选择。对于辩论来说,我们暂且说法拉利更快,但在没有交通的情况下才是正确的选择,因为它在交通中不会有效。人们应该更少关注某样东西的纸上资质(因为这不是“最强王牌”),更应该关注技术在问题域中的适用性,正如你的比喻所能证明的那样。 - Adam Houldsworth
显示剩余3条评论

26
大约在2005年,来自本机/托管 fence 两侧的两位微软性能专家试图回答同一个问题。他们的方法和过程仍然很有趣,结论至今仍然成立 - 我不知道是否有更好的尝试给出了一个知情的答案。他们指出,讨论差异性能的潜在原因是假设和无用的,真正的讨论必须有一些实证基础来说明这种差异的现实世界影响。
因此,老新Raymond ChenRico Mariani为友好竞争制定了规则。一个中英词典被选择作为玩具应用场景:足够简单以编写为业余副项目,但复杂到足以展示非平凡的数据使用模式 。规则开始简单 - Raymond 编写了一个直接的 C++ 实现,Rico 将其逐行迁移到 C#,没有任何复杂性,然后两个实现运行了基准测试。之后,进行了几次优化迭代。完整详情请查看:1234567891011121314

这场泰坦之间的对话非常有教育意义,我全心全意地推荐您深入研究一下——但如果您没有时间或耐心,Jeff Atwood 精彩地总结了底线

enter image description here

最终,C++ 的速度是原来的两倍,但起初它慢了 13 倍。

正如 Rico 总结的那样

所以我会因为惨败而感到羞愧吗?当然不会。托管代码几乎没有付出太多的努力就取得了非常好的结果。要击败托管版本,Raymond 必须:

  • 编写自己的文件/io模块

  • 编写自己的字符串类

  • 编写自己的分配器

  • 编写自己的国际映射

当然,他使用了可用的低级库来完成这项工作,但这仍然是很多工作。您能称剩下的部分为STL程序吗?我不这么认为。

11年过去了,C#/C++版本不计其数,但这仍然是我的经验。

当然,这并非巧合,因为这两种语言都以惊人的方式实现了它们迥异的设计目标。C#希望在开发成本是主要考虑的情况下使用(仍然是大多数软件),而C++则在您不惜一切代价挤出机器的每一点性能的场景下脱颖而出:游戏、算法交易、数据中心等。


1
这是最好的答案,他总结得非常漂亮! - Rodney P. Barbati

23

C++在性能方面始终具有优势。使用C#,我不需要处理内存,而且有大量的资源可供我使用,以便完成工作。

你需要考虑的更多是哪种语言可以节省你的时间。现在机器非常强大,大多数代码应该使用最少的时间获取最大的价值。

如果在C#中存在核心处理过程需要太长时间,那么您可以使用C++构建,并通过C#进行互操作。

停止考虑你代码的性能。开始创造价值。


15
+1表示赞同,“不要再想着如何提高代码性能,开始着手构建价值。” - Thomas Matthews
2
我同意C#的开发速度更快,这在很多情况下都更有价值。 - Yochai Timmer
开发人员需要为每种情况做出明智的选择。在某些情况下,C# 无法满足需求。例如计算型应用程序,如视频转换器、视频混合器、3D 虚拟化、流计算或游戏。此外,您必须注意项目目标硬件。有些应用程序必须在低端硬件上运行。 - S.M.Mousavi

7

C#比C++更快,编写速度更快。对于执行时间,没有什么比分析器更好了。

但是C#没有像C++那样可以轻松接口的库。

C#也严重依赖于Windows操作系统...


5
C#需要编译器、操作系统和平台提供更多的支持,这对于嵌入式系统来说是一个巨大的问题。 - Thomas Matthews
C# 不仅可以在 Windows 上运行,还可以在 Mac、Linux、Android、iOS 等平台上运行。请参见 http://www.microsoft.com/net/multiple-platform-support、http://xamarin.com/platform、http://www.mono-project.com/docs/about-mono/supported-platforms。 - yoyo
@yoyo:C#确实可以在其他平台上运行(Linux下也有Mono),但你可能想要使用的库(例如Windows Forms)可能不支持。可移植性是C#开发中的一个问题。 - Alexandre C.
1
这个答案现在已经改变了。微软目前正式支持在Linux上运行.NET Standard运行时。因此,除非你必须在微控制器上开发,否则可移植性根本不是问题。更不用说它的速度也快了很多。 - MovGP0

5

顺便提一句,对于时间关键型应用程序,通常不会使用C#或Java进行编码,主要是因为垃圾回收的执行时间无法确定。

在现代,应用程序或执行速度不像以前那样重要。开发进度、正确性和稳健性更加优先。如果一个应用程序有很多错误、经常崩溃甚至错过了上市或部署的机会,那么高速版本也没有什么用处。

由于开发进度是一个优先考虑的问题,出现了新的语言来加快开发。C#就是其中之一。C#通过从C++中删除引起常见问题的功能(例如指针)来帮助提高正确性和稳健性。

使用C#或使用C++开发的应用程序的执行速度差异在大多数平台上都可以忽略不计。这是因为执行瓶颈不是与语言相关的,而通常取决于操作系统或I/O。例如,如果C++在5毫秒内执行一个函数,而C#只需2毫秒,但等待数据需要2秒钟,则在函数中花费的时间与等待数据的时间相比微不足道。

选择最适合开发人员、平台和项目的语言。朝着正确性、稳健性和部署的目标努力。将应用程序的速度视为错误:将其列为优先事项,与其他错误进行比较,并根据需要进行修复。


我的理解是,C#(Java也是?)并没有完全“删除”指针的存在/概念,而是通过实际语法的某种抽象来隐藏它们,使程序员看不到。虽然这是一个相当无关紧要的点。C#的相对简单性是不可争议的。 - Mike G

3
一个更好的看待它(C/C++)的方式是把所有东西都视为比较慢,因为它会抽象化而不是按照"棒和泥"的范式进行操作。这就是所谓的系统编程,你要在逆境或裸机环境下进行编程。通过这样做,你也能够获得其他语言如C#或Java无法达到的速度。但遗憾的是C根源于以艰难的方式解决问题,因此您 mostly 会编写更多的代码并花费更多的时间进行调试。
C也区分大小写,C ++中的对象也遵循严格的规则集。例如,紫色的冰淇淋可能与蓝色的冰淇淋不同,尽管它们可能是锥体,但它们不一定属于锥形家族,如果您忘记定义什么是锥体,程序就会出错。因此,冰淇淋的属性可能会或可能不会相同。现在是关于速度的论点,C / C ++使用栈和堆方法,这是裸机获取性能的方法。
使用Boost库,您可以实现令人难以置信的速度,不幸的是,大多数游戏工作室仍然坚持使用标准库。这可能是因为用C / C ++编写的软件通常在文件大小上非常庞大,因为它是一组巨大的文件集合,而不是一个单独的文件。此外,请注意所有操作系统都是使用C编写的,因此通常为什么我们还需要问什么可能更快呢?
此外,缓存并不比纯内存管理更快,抱歉,但这只是不可行的。内存是一些物理实体,缓存是一些软件以获得性能提升所做的事情。也可以认为,如果没有物理内存,缓存根本不存在。这并不排除了必须在某个级别上对内存进行管理,无论是自动化还是手动化。

1

要得到你问题的确切答案,除非在特定系统上进行基准测试,否则不太可能。然而,思考一些像C#和C++这样的编程语言之间的一些基本差异仍然是有趣的。

编译

执行C#代码需要一个额外的步骤,即JIT编译。就性能而言,这将有利于C++。此外,JIT编译器只能在JIT编译的代码单元(例如方法)内优化生成的代码,而C++编译器可以使用更激进的技术跨方法调用进行优化。

然而,JIT编译器能够优化生成的机器代码以紧密匹配底层硬件,从而利用额外的硬件功能。据我所知,.NET JIT编译器并没有这样做,但它理论上可以为Atom和Pentium CPU生成不同的代码。

内存访问

垃圾回收架构在许多情况下可以创建比标准C++代码更优化的内存访问模式。如果用于第一代的内存区域足够小,则可以保持在CPU缓存内,从而提高性能。如果您创建和销毁大量小对象,则维护托管堆的开销可能比C++运行时所需的开销小。同样,这高度依赖于应用程序。 Python性能研究表明,由于更优化的内存访问模式,特定的托管Python应用程序能够比编译版本更好地扩展。


JIT编译代码时需要额外的步骤,但由于重用规则,您可能会重复使用那段代码比单次编译多数千次。(例如:循环)。因此,问题在于JIT是否做得足够好来弥补最初的编译。 - Yochai Timmer

1
为什么要用C++编写一个不需要太多优化的小应用程序,如果有更快的途径(如C#)呢?

为什么?因为我喜欢看到我的代码运行。包括析构函数。即使是一个小应用程序,也受益于针对可读性进行优化。 - IInspectable

0

我专注于优化约15年,经常重写C++代码,尽可能地使用编译器内置函数,因为C++的性能通常远远不及CPU的能力。需要考虑缓存性能。许多向量数学指令需要替换标准的C++浮点代码。 大量STL代码被重新编写,通常运行速度快得多。数学和大量使用数据的代码可以通过重写获得惊人的结果,因为CPU接近其最佳性能。

在C#中,这一切都是不可能的。比较它们相对实时性能的问题真的是一个令人震惊的无知问题。在C++中,最快的代码将是每个单独的汇编指令都针对手头的任务进行了优化,没有不必要的指令-完全没有。每个内存块在需要时使用,并且不会因为语言设计而复制n次。每个所需的内存移动与缓存协调工作。 当精确的实时要求考虑到准确性和功能时,最终算法不能再改进。

然后你将接近最优解决方案。

与这种理想情况相比,C#的表现令人震惊。C#无法竞争。事实上,我目前正在重新编写一大堆C#代码(当我说重新编写时,我指的是完全删除和替换它),因为它甚至在重载实时性能方面都不在同一个城市,更别提球场了。

所以,请不要自欺欺人。C#很慢。非常慢。所有软件都在减速,而C#正在加剧这种速度下降。所有软件都使用汇编语言中的取指执行周期(你知道的-在CPU上)。如果您使用10倍的指令,则速度会变慢10倍。如果您破坏缓存,则速度会更慢。如果您将垃圾收集添加到实时软件中,那么您经常会被愚弄成认为代码运行“还好”,只是偶尔会有那么几个时刻,代码会“有点慢一段时间”。

尝试在每个周期都很重要的代码中添加垃圾回收系统。我想知道股票交易软件是否有垃圾回收(你知道 - 在花费了3亿美元的新海底电缆上运行的系统上)。我们能否每2秒节省300毫秒?航天飞机上的飞行控制软件怎么样?性能车辆中的发动机管理软件呢?(一个赛季的胜利可能价值数百万)。

实时垃圾回收是完全失败的。

所以,强调一下,C++要快得多。C#是一个倒退。


2
C#并不是一个倒退的语言,因为C#的设计需要满足与C++完全不同的一组要求。C#以执行速度换取开发速度。你所指出的那些C#会失败的例子(如航天飞机、引擎管理等)从未是C#的目标应用。C#允许相对快速地交付完成的产品,并且可以在其虚拟机运行的任何硬件上运行该产品。如果您知道您的代码需要在数百种不同的硬件配置上运行,您能编写汇编优化吗? - mag_zbc
我同意你所说的mag_zbc,但原问题是“C#真的比C++慢吗?”而我的“向后跳”的评论是针对C#的速度,而不是它的可移植性。 - David Lloyd
这是一个有偏见的观点,基于纯粹的推测性假设,没有提供任何基准。 - zmechanic

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