为什么Java可能比C ++更快?


79

在基准测试中,Java有时会胜过C ++。当然,有时C ++的表现会更好。

请参阅以下链接:

但这怎么可能呢?令我惊讶的是,解释的字节码可能比编译的语言还要快。

有人可以解释一下吗?谢谢!


2
您可以看一看shootout.alioth.debian.org/u32/…,看看在java / c ++上运行得更快的问题类型……看到问题的模式,而不是这些特定的问题……
c0da

2
请参见为什么Java的名声很慢?有关此主题的很多详细信息。
彼得Török

11
这是违法的(第10.101.04.2c),以产生一个Java VM执行比用C ++产生的可执行二进制更快。
Mateen Ulhaq 2011年

1
@muntoo你到底是什么意思?10.101.04.2c节是什么?
高地马克

3
@HighlandMark您不是圈子的成员。您不知道圈子里发生了什么。圆是绝对的-圆的定律取代了自然的定律。您不能违抗也不能质疑圈子。我是圈子,圈子就是我。
Mateen Ulhaq 2011年

Answers:


108

首先,大多数JVM都包含一个编译器,因此“解释的字节码”实际上非常少见(至少在基准代码中如此)在现实生活中并不罕见,因为在您的代码中,通常不只是几个琐碎的循环,而且这些循环经常重复出现)。

其次,涉及的许多基准似乎有很大的偏见(无论是出于意图还是出于无能,我不能说真的)。例如,几年前,我查看了您发布的链接之一中链接的一些源代码。它具有如下代码:

  init0 = (int*)calloc(max_x,sizeof(int));
  init1 = (int*)calloc(max_x,sizeof(int));
  init2 = (int*)calloc(max_x,sizeof(int));
  for (x=0; x<max_x; x++) {
    init2[x] = 0;
    init1[x] = 0;
    init0[x] = 0;
  }

由于calloc提供的内存已经归零,因此使用for循环将其再次归零显然是没有用的。之后(如果有内存可用),无论如何都要用其他数据填充内存(并且不依赖于将其置零),因此无论如何都完全不需要进行所有置零。用一个简单的代码替换上面的代码malloc(就像任何理智的人一样)可以提高C ++版本的速度,使其足以胜过Java版本(如果有内存的话,可以说是相当大的优势)。

考虑(作为另一个示例)methcall上一个链接中博客条目中使用的基准。尽管名称(以及外观可能如何),但C ++版本并没有真正衡量方法调用开销。关键的代码部分在Toggle类中:

class Toggle {
public:
    Toggle(bool start_state) : state(start_state) { }
    virtual ~Toggle() {  }
    bool value() {
        return(state);
    }
    virtual Toggle& activate() {
        state = !state;
        return(*this);
    }
    bool state;
};

关键是事实state = !state;。考虑一下当我们更改代码以将状态编码为int而不是时会发生什么bool

class Toggle {
    enum names{ bfalse = -1, btrue = 1};
    const static names values[2];
    int state;

public:
    Toggle(bool start_state) : state(values[start_state]) 
    { }
    virtual ~Toggle() {  }
    bool value() {  return state==btrue;    }

    virtual Toggle& activate() {
        state = -state;
        return(*this);
    }
};

微小的变化使整体速度提高了大约5:1。尽管基准旨在测量方法调用时间,但实际上,它所测量的大部分时间都是在int和之间转换的时间bool。我当然同意,不幸的是,原始代码显示的效率低下-但鉴于它在实际代码中似乎很少出现,并且很容易在发生时(如果确实发生)进行修复,那么我很难思考它的意义很大。

万一有人决定重新运行所涉及的基准测试,我还应该补充一点,就是对产生的Java版本进行了几乎相同的微不足道的修改(或至少一次生成了-我没有使用最近的JVM(以确认它们仍然可以使用))也对Java版本进行了相当大的改进。Java版本具有一个NthToggle :: activate(),如下所示:

public Toggle activate() {
this.counter += 1;
if (this.counter >= this.count_max) {
    this.state = !this.state;
    this.counter = 0;
}
return(this);
}

将其更改为调用基本函数而不是this.state直接进行操作可以大大提高速度(尽管不足以跟上修改后的C ++版本)。

因此,我们最终得到的是关于解释后的字节码与某些(我见过的)最差基准测试的错误假设。两者都没有给出有意义的结果。

我自己的经验是,在经验丰富的程序员同样重视优化的情况下,C ++往往会击败Java,但是(至少在这两者之间),C ++几乎不会像程序员和设计那样产生太大的影响。被引用的基准告诉我们的更多关于作者的能力(不称职)/(不诚实)诚实的信息,而不是他们声称的基准语言。

[编辑:正如上面的某处所暗示,但从未像我应该直接指出的那样,我引用的结果是大约5年前使用当时最新的C ++和Java实现进行测试时得到的结果。我还没有使用当前的实现重新运行测试。乍一看,表明代码尚未修复,因此所有更改将是编译器掩盖代码中问题的能力。]

但是,如果我们忽略Java示例,实际上解释后的代码可能比编译后的代码运行得更快(尽管很困难,而且有些不寻常)。

发生这种情况的通常方式是,所解释的代码比机器代码紧凑得多,或者它在数据缓存比代码缓存大的CPU上运行。

在这种情况下,小型解释器(例如,Forth实现的内部解释器)可能完全适合代码缓存,而其解释的程序完全适合数据缓存。缓存通常比主内存快至少10倍,并且通常要快得多(100倍也不再罕见)。

因此,如果高速缓存比主内存快N倍,并且实现每个字节代码所需的机器代码指令少于N个,那么字节代码应该会获胜(我正在简化,但我认为总体思路仍然应该显而易见)。


26
+1,完全确认。尤其是“语言很少会像程序员和设计那样产生很大的影响”-您经常会发现可以优化算法的问题,例如,改进big-O会比最佳编译器提供更多的提升。
schnaader 2011年

1
“万一有人决定重新运行所涉及的基准测试……”不要!早在2005年,这些旧任务就被废弃了,取而代之的是现在基准测试游戏中显示的任务。如果有人想重新运行某些程序,然后请重新运行的基准测试游戏主页显示当前任务的当前计划shootout.alioth.debian.org
igouy

@igouy:有些人可能想简单地确认/否定他们所运行的基准测试的结果,而至少需要进行最少的校正才能使他们与现实之间保持最小的联系。同时,您基本上是对的:相关基准非常糟糕,仅解决最明显的错误将无济于事。
杰里·科芬

这就是为什么在2005年,它们被废弃并被基准测试游戏中现在显示的任务所取代。谁都不知道的人最好重新运行那些旧程序。
igouy

13
+1我不喜欢人们用C或Java风格编码C ++,然后声称Java更出色。免责声明:我不会说任何一种语言都是高级语言,但是以一种可能完全适合另一种语言的方式编写糟糕的C ++代码并不能使这两种语言具有可比性。
Christian Rau

111

由专家以无限制的时间完成的手工C / C ++ 将至少与Java一样快或更快。最终,Java本身是用C / C ++编写的,因此,如果您愿意付出足够的工程精力,那么您当然可以做Java所做的一切。

但是实际上,由于以下原因,Java通常执行速度非常快:

  • JIT编译 -尽管Java类存储为字节码,但通常会在程序启动时由JIT编译器编译为本地代码。编译后,它就是纯本机代码-因此,从理论上讲,一旦程序运行了足够长的时间(即,在完成所有JIT编译之后),它的性能将与已编译的C / C ++相同。
  • Java中的垃圾回收非常快速且高效-热点GC可能是世界上最好的全方位GC实现。这是Sun和其他公司经过多年的专家努力的结果。在C / C ++中使用的任何复杂的内存管理系统几乎都会变得更糟。当然,您可以使用C / C ++编写非常快速/轻量级的基本内存管理方案,但是它们不会像完整的GC系统那样通用。由于大多数现代系统都需要复杂的内存管理,因此Java在实际情况下具有很大的优势。
  • 更好的平台定位 -通过将编译延迟到应用程序启动(JIT编译等),Java编译器可以利用以下事实:它知道在其上执行的确切处理器。这样可以实现一些非常有益的优化,而这些优化在需要针对“最低公分母”处理器指令集的预编译C / C ++代码中无法实现。
  • 运行时统计信息 -因为JIT编译是在运行时完成的,所以它可以在程序执行时收集统计信息,从而可以进行更好的优化(例如,了解采用特定分支的概率)。这可以使Java JIT编译器产生比C / C ++编译器更好的代码(C / C ++编译器必须提前“猜测”最可能的分支,而这一假设通常是错误的)。
  • 很好的库 -Java运行时包含许多写得很好的库,它们具有良好的性能(特别是对于服务器端应用程序)。这些通常比您自己编写或轻松获得C / C ++更好。

同时C / C ++也有一些优点:

  • 更多时间进行高级优化 -C / C ++编译一次完成,因此如果进行配置,则可能花费大量时间进行高级优化。Java不能做到这一点没有理论上的原因,但是在实践中,您希望Java相对快速地JIT编译代码,因此JIT编译器倾向于将重点放在“更简单”的优化上。
  • 字节码中无法表达的指令 -尽管Java字节码是完全通用的,但仍有一些您可以在字节码中无法做到的低级操作(未经检查的指针算术就是一个很好的例子!)。通过使用这些技巧,您可以获得一些性能优势
  • 更少的“安全性”约束 -Java做一些额外的工作来确保程序安全可靠。例如数组的边界检查,某些并发保证,空指针检查,类型转换的类型安全性。通过在C / C ++中避免这些检查,可以获得一些性能提升(尽管可以说这是一个坏主意!)

总体:

  • Java和C / C ++可以达到相似的速度
  • C / C ++在极端情况下可能会略有优势(例如,AAA游戏开发人员仍然喜欢它,这并不奇怪)
  • 实际上,这将取决于上面列出的不同因素如何为您的特定应用程序平衡。

9
广告“在C ++中花更多时间进行优化”:这是Oracle VM在选择服务器VM时所做的调整之一:它接受更高的启动成本,以便从长远来看可以提供更高的性能。但是,已对客户端VM进行了调整,以实现最佳启动时间。这样的区分,即使存在的Java。
约阿希姆·绍尔

8
-1:C ++编译器创建一个非常优化的二进制文件可能需要更多时间(从字面上看,对于一个大型库而言,是数小时)。Java JIT编译器甚至不需要花费很多时间,即使是“服务器”版本。我严重怀疑Java JIT编译器是否能够像MS C ++编译器那样执行整个程序优化。
quant_dev

20
@quant_dev:可以,但是那不是我在回答中所说的作为C ++的优势(更多时间进行高级优化)吗?那么为什么是-1?
mikera 2011年

13
垃圾回收对于Java而言不是速度优势。如果您是不知道自己在做什么的C ++程序员,那么这只是速度上的优势。如果您要检查的是可以分配的速度,那么可以,垃圾收集器将获胜。但是,通过手动管理内存仍然可以更好地完成程序的整体性能。
Billy ONeal

4
... 但是,对于C ++,理论上您总是可以放置一个“类似于JIT的层”,该层在运行时进行类似的分支优化,同时保持C ++程序的原始速度。(理论上。:()
Mateen Ulhaq 2011年

19

Java运行时的心不是解释字节码。而是使用所谓的“及时编译”。基本上,在程序运行时,它将获取字节码并将其转换为针对特定CPU优化的本机代码。


实际上,是的。原则上,这取决于-早期的Java虚拟机使用字节码解释器,如果看上去足够努力,您可能仍然可以找到字节码解释器VM。
Steve314 2011年

10
@ Steve314:但是,纯粹解释性的VM 不会胜过C ++,因此它们与这个问题并不真正相关。
约阿希姆·绍尔

JIT编译器还可以针对代码的特定用途动态优化,而对于静态编译的代码则无法实现。
starblue 2011年

2
@starblue,嗯,使用静态编译在某种程度上是可能的-请参阅配置文件引导的优化。
SK-logic

18

所有人都是平等的,您可能会说:不,Java永远不应该更快。您总是可以从头开始用C ++实现Java,从而至少获得同样好的性能。但是在实践中:

  • JIT 在最终用户的计算机上编译代码,从而使其能够针对正在运行的确切CPU进行优化。虽然这里有编译的开销,但对于密集型应用程序来说,它可能很值得。通常,现实生活中的程序并未针对您使用的CPU进行编译。
  • 与C ++编译器相比,Java编译器在自动优化方面可能会更好。也许不是,但是在现实世界中,事情并不总是完美的。
  • 由于其他因素(例如垃圾收集),性能行为可能会有所不同。在C ++中,通常在处理完对象后立即调用析构函数。在Java中,您只需释放该引用,从而延迟实际销毁。这是在性能方面既不存在也不存在的另一个示例。当然,您可以争辩说可以用C ++实现GC并用它来完成,但是现实是很少有人愿意/可以/可以。

顺便说一句,这让我想起了80年代/ 90年代关于C的辩论。每个人都想知道“ C会和汇编一样快吗?”。基本上,答案是:纸上没有,但是实际上C编译器比90%的汇编程序员创建的代码效率更高(嗯,一旦成熟一点)。


2
关于GC,不仅仅是GC可能会延迟对象的销毁(从长远来看这无关紧要)。事实是,对于现代GC,与C ++相比,Java中短期对象的分配/取消分配非常便宜。
彼得Török

@PéterTörök是的,您是对的,很好。
Daniel B

9
@PéterTörök但是,在C ++中,短期对象通常放在堆栈上,这反过来比Java可以使用的任何GC堆快得多。
quant_dev

@quant_dev,您忘记了另一个重要的GC效果:压缩。因此,我不确定哪种方法更快。
SK-logic

3
@DonalFellows是什么让您觉得我不得不担心C ++中的内存管理?大多数时候我都不喜欢。您需要应用一些简单的模式,这些模式与Java不同,仅此而已。
quant_dev

10

但是分配只是内存管理的一半-释放是另一半。事实证明,对于大多数对象而言,直接垃圾回收成本为零。这是因为复制收集器不需要访问或复制死对象,而只需访问活动对象。因此,分配后不久变成垃圾的对象不会对收集周期造成任何工作负担。

...

JVM惊人地擅长弄清我们曾经假设只有开发人员才能知道的事情。通过让JVM根据具体情况在堆栈分配和堆分配之间进行选择,我们可以获得堆栈分配的性能优势,而无需让程序员为在堆栈上分配还是在堆上分配分配而烦恼。

http://www.ibm.com/developerworks/java/library/j-jtp09275/index.html


这只是整个图片的一部分,但仍然很相关。
Joachim Sauer

2
我喜欢它的实质:java是面向新手的,相信神奇的GC,它知道得更好。
Morg。

1
@Morg:或者你可以这样看:Java是为喜欢完成事情而不是浪费时间和手动内存管理而浪费时间的人的。
兰代

4
@Landei我认为,如果使用Java编写任何体面的长期广泛使用的代码库,那么您的评论将更具信誉。在我的世界中,真正的OS用C编写,postgreSQL用C编写,因为最重要的工具确实很难重写。Java曾经(甚至是正式版本)使技能不高的人员能够在畜群中编程,但仍能取得切实的成果。
Morg。

1
@Morg我觉得很奇怪,您似乎只专注于OS。由于多种原因,这根本不是一个好方法。首先,操作系统的要求与大多数其他软件有着根本的不同,其次,您具有熊猫拇指法则(谁想要用另一种语言重写完整的操作系统,谁想要在有工作甚至免费替代方案的情况下编写自己的操作系统?)其他第三种软件则使用该操作系统的功能,因此无需编写磁盘驱动程序,任务管理器等。如果您不能提供一些更好的参数(并非完全基于操作系统),那听起来就很讨厌。
兰代

5

尽管完全优化的Java程序很少会击败完全优化的C ++程序,但是内存管理等方面的差异可以使许多在Java中惯用地实现的算法比在C ++中惯用地实现的算法更快。

正如@Jerry Coffin所指出的那样,在许多情况下,简单的更改可以使代码更快—但是,通常对于一种或另一种语言,可能需要进行太多不整洁的调整才能使性能提高值得。这可能是您在一个良好的基准测试中看到的,该基准测试表明Java的性能优于C ++。

而且,尽管通常并不是那么重要,但是有些性能优化是Java之类的JIT语言所能做到的,而C ++无法做到。Java运行时可以在代码编译进行改进,这意味着JIT可以潜在地生成经过优化的代码,以利用新的(或至少是不同的)CPU功能。因此,使用10年的Java二进制文件可能会超过使用10年的C ++二进制文件。

最后,在极少数情况下,从总体上讲,完整的类型安全性可以极大地提高性能。由于无需硬件进程边界或昂贵的上下文切换,Singularity是一种几乎完全用基于C#的语言编写的实验性OS,具有更快的进程间通信和多任务处理能力。


5

Tim Holloway在JavaRanch上发布了:

这是一个原始示例:回到机器以数学确定的周期运行时,分支指令通常具有2个不同的时序。一个用于分支的使用时间,一个用于分支的不使用时间。通常,无分支情况会更快。显然,这意味着您可以基于哪种情况更常见的知识来优化逻辑(但要受约束的是,我们“知道”的并不总是实际情况)。

JIT重新编译使这一步骤更进一步。它监视实际的实时使用情况,并根据实际最常见的情况翻转逻辑。如果工作负载转移,请再次翻转。静态编译的代码无法执行此操作。这就是Java有时可以胜过手工调整的汇编/ C / C ++代码的方式。

资料来源:http : //www.coderanch.com/t/547458/Performance/java/Ahead-Time-vs-Just-time


3
再一次,这是错误的/不完整的。具有配置文件引导的优化的静态编译器可以识别这一点。
康拉德·鲁道夫

2
康拉德(Konrad),静态编译器可以根据当前的工作量翻转逻辑吗?据我了解,静态编译器只生成一次代码,并且永远保持不变。
Thiago Negri

2
当前的工作量,否。但是典型的工作量。概要文件引导的优化可以分析程序在典型负载下的运行方式,并相应地优化热点,就像HotSpot JIT一样。
康拉德·鲁道夫

4

这是因为生成机器代码的最后一步在运行Java程序时在JVM 内部透明地发生,而不是在构建C ++ proram时显式地发生。

您应该考虑这样一个事实,现代JVM花费大量时间动态地将字节代码编译为本地机器代码,以使其尽可能快。这使JVM可以执行各种编译器技巧,通过了解正在运行的程序的分析数据,这些技巧甚至可以更好。

诸如自动内联getter这样的事情,这样就不需要JUMP-RETURN来获取值,从而加快了工作速度。

但是,真正允许快速程序的是事后进行更好的清理。Java中的垃圾回收机制比C语言中的无malloc手动机制。许多现代的无malloc实现都在其下使用垃圾收集器。


请注意,这些内嵌的东西使启动JVM的速度变得更大,更慢,直到更好的代码有机会赶上来。

1
“许多现代的无malloc的实现都在下面使用垃圾回收器。” 真?我想了解更多;你有参考吗?
肖恩·麦克米兰

谢谢。我试图找到一种说法,说JVM不再只包含即时编译器编译为可执行代码,而是一个热点编译器,它对正在运行的代码进行概要分析并因此进行了进一步优化。我像C ++这样的一次性编译器很难做到这一点。
高地马克

@SeanMcMillan,我看了一段时间的关于无malloc实现的性能分析,其中提到最快的是在下面使用垃圾收集器。我不记得我在哪里读书。

是BDW保守GC吗?
黛咪

4

简短的回答-不是。忘记了,这个话题就像火或车轮一样古老。Java或.NET并非也不会比C / C ++快。对于大多数您根本不需要考虑优化的任务,它的速度足够快。类似于表单和SQL处理,但这就是结束的地方。

对于基准测试或无能的开发人员编写的小型应用程序,是的,最终结果将是Java / .NET可能会接近甚至更快。

实际上,简单的事情(例如在堆栈上分配内存或仅使用memzones)将立即杀死Java / .NET。

垃圾收集世界正在使用某种记账方式进行所有核算。将memzone添加到C中,C会当场在那里更快。尤其是对于那些Java与C的“高性能代码”基准测试,如下所示:

for(...)
{
alloc_memory//Allocating heap in a loop is verrry good, in't it?
zero_memory//Extra zeroing, we really need it in our performance code
do_stuff//something like memory[i]++
realloc//This is lovely speedup
strlen//loop through all memory, because storing string length is soo getting old
free//Java will do that outside out timing loop, but oh well, we're comparing apples to oranges here
}//loop 100000 times

尝试在C / C ++(或新的放置位置)中使用基于堆栈的变量,它们会转换为sub esp, 0xff,这是一条x86指令,用Java击败了-您无法...

大多数时候,我看到那些将Java与C ++进行比较的工作台,这使我走得很近?错误的内存分配策略,没有储备的自增长容器,多个新容器。这甚至与面向性能的C / C ++代码并不接近。

也是一本好书:https : //days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf


1
错误。完全错误。您将无法通过手动内存管理胜过压缩GC。天真的引用计数永远不会比正确的标记扫描更好。一旦涉及到复杂的内存管理,C ++就是一个障碍。
SK-logic

3
@ SK-Logic:错误,对于内存区或堆栈分配,根本没有内存分配或释放。您有一块内存,只需写入即可。使用并发保护InterlockedExchange等易失变量将块标记为空闲,如果下一个线程认为它是空闲的,则仅将其数据转储到预分配的块中,而无需将其转至OS进行存储。使用堆栈可以更加轻松,唯一的例外是您不能在堆栈上转储50MB。而且该对象的生命周期仅在{}内部。
编码员

2
@ SK-logic:编译器首先是正确性,其次是性能。搜索引擎,数据库引擎,实时交易系统,游戏是我认为对性能至关重要的东西。而且大多数依赖扁平的结构。无论哪种方式,编译器大部分都是用C / C ++编写的。我想使用自定义分配器。再说一次,我认为在rammap上使用树或列表元素没有问题。您只使用新的展示位置。这没有太多复杂性。
编码器

3
@ SK-logic:速度并不快,我见过的每个.NET / Java应用程序总是变慢了,而且是真正的猪。每次将托管应用程序重写为SANE C / C ++代码都可以使应用程序更干净,更轻便。托管应用程序总是很繁重。请参阅VS2010与2008。数据结构相同,但是VS2010是HOG。正确编写的C / C ++应用程序通常会在数毫秒内启动,并且不会卡在启动屏幕上,同时还会消耗更少的内存。唯一的缺点是您必须在编写代码时考虑到硬件,而且很多人不知道当今的情况。只有基准管理才有机会。
编码器

2
您的轶事证据不算在内。适当的基准测试可以显示出真正的差异。指的是绑定到庞大且次优的GUI库的GUI应用程序,这尤其奇怪。而且,更重要的是-从理论上讲,正确实施GC的性能极限要高得多。
SK-logic

2

实际情况是它们都是高级汇编程序,它们完全按照程序员的要求进行操作,从而使程序员按照程序员的要求准确地告诉他们。性能差异如此之小,以至于与所有实际目的无关。

语言不是“慢”,程序员编写了一个慢程序。除非有研究的作者愿意磨破自己的特殊斧头,否则很少有一种程序能够以一种语言编写出最好的方法,而该程序要优于(出于任何实际目的)使用另一种语言的最好方法来完成相同的事情。

显然,如果您要处理像硬实时嵌入式系统这样的罕见情况,那么语言选择可能会有所作为,但是这种情况多久发生一次呢?在这些情况下,正确选择的频率不会一目了然。


2
从理论上讲,“理想的” JITting VM 必须通过将其优化调整为动态收集的概要分析信息,从而胜过静态编译的代码。实际上,JIT编译器还不那么聪明,但是它们至少能够生成质量与较大和较慢的静态同级设备相似的代码。
SK-logic

2

请参阅以下链接...但这怎么可能?令我惊讶的是,解释的字节码可能比编译的语言还要快。

  1. 这些博客帖子是否提供可信赖的证据?
  2. 这些博客文章是否提供确定的证据?
  3. 这些博客文章甚至提供有关“解释的字节码”的证据吗?

Keith Lea告诉您存在“明显的缺陷”,但对那些“明显的缺陷”不做任何事情。早在2005年,这些旧任务就被废弃了,取而代之的是现在基准测试游戏中显示的任务。

Keith Lea告诉您,他“从已经过时的Great Computer Language Shootout中获取了C ++和Java的基准代码,并运行了测试”,但实际上,他只显示了25个过时测试中的14个

Keith Lea现在告诉您,七年前他并没有试图在博客文章中证明任何东西,但是那时他说:“我讨厌听到人们说Java速度很慢,而我知道它相当快……”,这表明那时,他在试图证明一些东西。

克里斯蒂安·费尔德(Christian Felde)告诉您:“我没有创建代码,只是重新运行测试。” 似乎使他免于承担决定公开评估Keith Lea选择的任务和程序的决定的任何责任。

对25个微小程序的测量是否可以提供确定的证据?

这些度量是针对以“混合模式”运行的程序的,而不是Java的解释Java-“记住HotSpot的工作方式”。您可以轻松地找出Java在“解释的字节码”上的运行情况,因为您可以强制Java 解释字节码-只需对一些带有-Xint选项和不带-Xint选项的Java程序进行计时即可。


-1

这使我感到奇怪,这种“解释后的字节码”怪异的概念是如此普遍。你们是否听说过JIT编译?您的论点不能应用于Java。

但是,撇开JVM而言,在某些情况下,直接线程代码甚至是琐碎的字节码解释都可以轻松胜过高度优化的本机代码。解释非常简单:字节码可以非常紧凑,并且当同一算法的本机代码版本最终在一次迭代中遇到多个缓存未命中时,字节码将适合您的微小缓存。


解释的普遍性可能是由于精通计算机科学的人们所致。Java虚拟机是一台接受Java字节码并在能够本地运行Java字节码的机器/ not /上运行它的机器,而不能够编写功能上等效的本地程序。因此,它是翻译。您可以给它的缓存技术指定任何您可以想到的名称,无论是JIT还是其他名称,但是根据解释器的定义,它是一个解释器。
thiton 2011年

@thiton,您自己的CS-fu可能很薄弱。JVM不做任何形式的解释(针对热点)-作为一个敢于提到CS的人,您必须知道它的含义,以及解释的操作语义与运行本机代码有何不同。但是,很可能您对CS的了解不足,无法将编译与解释区分开。
SK-logic

2
嗯,但是要运行该字节码,必须将其转换为本地代码-您无法将Java字节码提供给CPU。因此,size参数无效。
quant_dev

@quant_dev,当然-我说这种情况与JVM完全无关。您需要一个更简单的字节码引擎才能使该效果正常运行。
SK-logic

-1

除了JIT,GC等之外,C ++可以非常轻松地变得比Java更慢。这不会在基准测试中显示,但是Java开发人员和C ++开发人员编写的同一个应用程序在Java中可能要快得多。

  • 操作员超载。每个简单的运算符(例如“ +”或“ =“)都可以调用数百行代码来进行安全检查,磁盘操作,日志记录,跟踪和性能分析。而且它们非常易于使用,一旦您使运算符超载,就可以自然而又大量地使用它们,而无需注意用法的累积方式。
  • 模板。这些对速度的影响不如内存。模板的不谨慎使用将导致生成数百万行代码(基本模板的替代方案),而您根本不会注意到它们。但是随后是二进制加载时间,内存使用量,交换使用量-所有这些也违反基准。RAM的使用率很高。

至于高级继承模式,它们非常相似-C ++具有Java所没有的,反之亦然,但是所有这些都引入了相似的,显着的开销。因此,在大量对象编程中没有特殊的C ++优势。

还有一个警告:与手动管理分配相比,GC可以更快或更慢。如果您分配了许多小对象,则在GC环境中通常会分配一块内存,并根据需要为新对象分派内存。在托管状态下-每个对象=单独分配需要大量时间。OTOH,如果您一次分配malloc()大量内存,然后仅手动将其分配给您的对象,或者使用几个较大的对象实例,则可能会更快。


4
我不同意这两点。是否使用运算符或方法都无关紧要。你说他们会扩散。废话–不超过方法;您可以打电话给他们,也可以不用。模板所产生的代码只不过是多次手写多次手写特定代码。可能有比运行时分派(虚拟函数)更多的代码,但这也将是无关紧要的:指令高速缓存行的性能在紧密循环中最重要,这里将只使用一个模板实例化,因此没有相关的内存压力由于模板。
康拉德·鲁道夫

通常的心态是方法昂贵,操作员廉价。您可以在需要时使用方法,在需要节省时间和优化时使用运算符。这不是技术问题,而是心理问题–并不是操作员“更重”,他们更易于使用且使用频率更高。(另外,您可以在现有代码中重载一个常用的运算符,使其与原始代码相同,外加一个额外的代码-突然,整个代码将大大减慢速度。)
SF.11

我认为这个心理事实是真实的,即使是事实,您也没有选择:如果需要功能,就可以使用它,而不管它是封装在运算符还是方法中。心理学与您选择的语义无关。
康拉德·鲁道夫

1
技巧问题。我会完全不去猜测,我会测量一下,然后采取行动。我从来没有对此策略有任何疑问。
康拉德·鲁道夫

1
@KonradRudolph:在清晰度和易于编写代码方面,这都是正确的,使其无缺陷且可维护。尽管如此,关于算法实现效率的观点仍然存在:如果您要obj.fetchFromDatabase("key")在五行代码中为同一键编写三遍,那么您将三思而后行是否一次获取该值并将其缓存在局部变量中。如果你写obj->"key"->超负荷充当数据库中提取,你是远更容易就让它通过,因为操作成本并不明显。
SF。

-2

不知何故,Stack Exchange不会占用我的其他堆栈点,所以...不幸的是没有回复...

但是,以我的拙见,这里投票得第二多的答案充满了错误信息。

C / C ++专家亲自编写的应用程序总是比Java应用程序快得多。没有“像Java或Faster一样快”。只是因为您在下面引用的内容,它的速度更快:

JIT编译:您是否真的希望自动优化器具有专家程序员的才能,并看到意图和CPU真正要运行的代码之间的链接?此外,与已经编译的程序相比,您所做的所有JIT都浪费了时间。

垃圾收集是一种工具,它以某种或多或少有效的方式简单地重新分配程序员本来会忘记分配的资源。

显然,这仅比C程序员处理您的内存的专家(您所选择的术语)要慢(并且在正确编写的应用程序中不存在泄漏)。

性能优化的C应用程序知道它正在运行的CPU,已经在其上编译过,否则这意味着您没有完全采取所有措施来提高性能吗?

运行时统计信息 这超出了我的知识,但是我确实怀疑C方面的专家拥有足够多的分支预测知识,无法再次超过自动化优化-

很好的库 Java中有很多未非常优化的函数可通过Java库轻松获得,在任何语言中都是如此,但是最优化的库是用C编写的,尤其是用于计算的库。

JVM 是一个抽象层,它不仅意味着很多优点,而且也意味着整个解决方案在设计上较慢。

总体:

由于Java在具有许多保护,功能和工具的JVM中的工作方式,Java永远无法达到C / C ++的速度。

C ++在优化的软件(无论是用于计算还是游戏)中具有明确的优势,通常看到C ++实现在编码竞赛中胜出,以至于最好的Java实现只能在第二页上看到。

在实践中,C ++并不是玩具,不会让您摆脱大多数现代语言可以处理的许多错误,但是由于更简单,更不安全,它固有地更快。

总结一下,我想说的是,大多数人对此不愿付出两分钱,最后,优化是一项仅保留给极少数幸运开发者的运动,除非真正关注性能的情况下(即,将硬件乘以10将无济于事-或至少代表数百万),大多数管理人员都喜欢未优化的应用程序和大量硬件。


再次。垃圾回收不仅是“释放资源的工具”。GC可以使您的结构紧凑。GC可以处理您的弱引用,并帮助您通过这种方式平衡缓存。多级GC使得堆分配多少 比你体积大,速度慢更便宜newmalloc()。通常,它比任何手动内存管理要快得多-因为您将无法手动重定位对象。因此,您所有的推理都是错误的和有偏见的。您对GC算法和JIT优化方法的知识太有限。
SK-logic

4
这个答案充满了对现代优化器功能的误解。手工优化的代码没有机会反对。但是,C ++ 有一个优化的编译器。
康拉德·鲁道夫

感谢您的评论SK-logic,但是正如您所说的,GC 通常可以快得多,我们正在谈论在特定情况下最快的方法,而且看来大多数人都同意GC可以做到的任何事情做程序员可以做到的,甚至更好。当然,当您具有直接内存访问权限时,您可以手动重定位对象。我对JVM内部的了解肯定是有限的,而且我确实希望Java负责人能够向我展示更多信息,而不只是告诉我有关GC能够做一些无法手动完成的事情的胡扯(大声笑...甚至GC都必须使用CPU指令;))。
Morg。

Konrad,我同意我在很大程度上低估了现代优化器……但是,我觉得有趣的是,您认为手工优化的代码不如自动优化的代码。您期望编译器看到人类不能做什么?
Morg。

1
对 。继续推-1,这不会改变C ++比Java更快的事实。我可能对现代编译器了解不多,但这与要点没有任何区别,要点是正确的,并且与此处投票最多的答案相矛盾。为什么在nVidia的HPC GPU上C ++会成为优先事项。为什么所有游戏都用C ++编写,为什么每个数据库引擎都用C编写?
Morg。

-4

我已经看过至少两个用Java完成的令人印象深刻的mmo,可以说它对游戏的速度不够快是一个错误的说法。仅仅因为游戏设计师比其他语言更喜欢C ++才表明它不仅与Java有关,还意味着程序员从未真正涉足任何其他编程语言/范例。像C / C ++甚至Java这样先进的语言中的任何内容,都可以产生在技术上可以满足或击败speed参数的代码。说得好,要归结为程序员所了解的知识,与哪些团队合作最重要,最重要的是为什么他们使用上述工具。由于我们正在处理编程的游戏开发方面,因此必须有更多的论点。简单地说 关于使用符合QA的工具的企业死于金钱和时间的问题,而在现实世界中,对于选择C ++而不是Java或任何其他语言的xx原因没有任何影响。这只是批量生产的决定。在我们所使用的最基本的计算算法水平上,是一和零,速度参数是有史以来应用于游戏的最愚蠢的参数之一。如果您想大幅提高速度,则可以完全放弃编程语言,并使用汇编程序,这可能是迄今为止的最大优势。


2
这堵文字墙似乎未添加其他答案中尚未说明的任何内容。请修改您的答案,使其更具可读性,并确保您的答案指向的是其他答案未提出的要点。否则,请考虑删除您的答案,因为这只会增加噪音。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.