为什么java中的这段代码比C++;和C#

为什么java中的这段代码比C++;和C#,java,c#,c++,c,performance,Java,C#,C++,C,Performance,我在做一个简单的家庭作业,我必须用C语言开发一个软件来找到100个最近的点 当我完成时,我很好奇地想知道,在启用了完整的VC++优化的情况下,用更多的点运行它需要多少时间。我试了10000次,大约用了8~9秒。然后我好奇地想知道C#和Java做同样的事情需要多少时间。正如预期的那样,C#花费的时间稍长,9~10秒;然而,Java只花了约400毫秒!为什么会这样 这是我用C、C和Java编写的代码: C: 有趣的是:首先,我之前提到的10000个点的文件,我用来做基准测试,实际上是另一个100个随

我在做一个简单的家庭作业,我必须用C语言开发一个软件来找到100个最近的点

当我完成时,我很好奇地想知道,在启用了完整的VC++优化的情况下,用更多的点运行它需要多少时间。我试了10000次,大约用了8~9秒。然后我好奇地想知道C#和Java做同样的事情需要多少时间。正如预期的那样,C#花费的时间稍长,9~10秒;然而,Java只花了约400毫秒!为什么会这样

这是我用C、C和Java编写的代码:

C:

有趣的是:首先,我之前提到的10000个点的文件,我用来做基准测试,实际上是另一个100个随机点的文件的100倍拷贝粘贴。像这样:

(Point 1)
(Point 2)
(Point 3)
(Point 1)
(Point 2)
(Point 3)
我认为不需要生成10000个随机点,因为代码无论如何都要遍历所有的数字,所以不会有什么不同(只会有更多的赋值)。但后来我决定生成10000个随机点,看看它们会如何反应:C和C#仍然在大约相同的时间内运行(增加约50毫秒);而另一方面,爪哇则增加了约500毫秒

另外,我认为值得注意的是,java在NetBeans中运行大约需要11秒(即使是在“运行”模式下,而不是在“调试”模式下)

我也尝试编译为C++而不是C,但没有什么区别。p> 我对C和C都使用VS 2015

以下是每种语言的设置:

C:

C#:

爪哇:

编辑: 好的,我按照建议重新做了测试:

首先,我在C和C中都使用了
结果
类/结构。我在java中使用它而不是在C/C中使用它的原因是java不能通过引用传递。
其次,我现在在main()函数中重复测试。
谢谢@Tony D抓住了那个虫子!:)

我不会发布代码,因为更改很小:只需在其他测试中完全实现java版本,这就是我所做的

这次我只测试了7000个点(不是10000个点)和30次迭代,因为测试需要很长时间,而且已经很晚了

结果变化不大:C#平均使用5228毫秒,C 4424毫秒和Java 223毫秒。Java仍然以20倍或更多的速度获胜

然后我尝试删除对Math.Pow的调用(只需更改
((p1.X-p2.X)*(p1.X-p2.X))+((p1.Y-p2.Y)*(p1.Y-p2.Y))
,然后一切都改变了。新的结果是:

爪哇:平均220毫秒

C#:平均195ms

C:平均195ms

如果我之前检查过的话:p

正如我所评论的,我考虑过这样做,但后来决定最好测试每个编译器内联函数的能力并优化这种简单调用。然而,当我得到这些奇怪的结果时,我本应该回去做这件事,但我太紧张了,以至于忘了做这件事

无论如何,说实话,我很惊讶Java编译器能够完全优化这行代码,而C#和C++却不能。虽然我知道C#上的角大小写检查和内部调用,但我发现Java编译器能够注意到该代码中不需要角大小写检查,这真的很有趣。

正如解释和检查的那样,在纯C中存在整数幂的no重载,如下所示:

double pow(double base, int exponent );
这意味着当您在C中调用
pow
时,它的处理方式类似于:

double pow(double base, double exponent) {
    return exp(log(base) * exponent);
}
此外,还应检查负基和整数幂的情况,这是以特殊方式处理的。在这里添加诸如
if(exponent==1.0)
if(exponent==2.0)
之类的条件不是一个好主意,因为这会减慢真正用于适当目的的数学代码的速度。因此,平方变得更慢

原则上,将
pow(x,2)
优化为
x*x
的唯一合理方法是让编译器识别此类反模式并为其生成特殊代码。它只是发生在这样的情况下,带有您的设置的C和C#编译器无法做到这一点,而Java编译器可以做到这一点。注意,它与内联功能无关:仅仅内联
exp(log(a)*b)
不会使代码更快


作为结论,我想指出的是,任何擅长编写数学代码的程序员都不会在自己的代码中编写
pow(x,2)

也许Java正在优化
Math.pow(p1.x-p2.x,2)
调用乘法:在C版本中,您可以显式地将其更改为乘法,看看这是否可以解释性能差异<代码> QueryPerformanceCounter < /C>也不可靠,这取决于您的操作系统版本和硬件,但是如果您的机器空闲,而且它不在程序中翻转内核,那么您就不应该受到bug的影响。java版本为什么有<代码>结果< /C> >类,但是C和C++版本不?你确定你是在比较苹果吗?这3个程序的输出是否相等?这是在强制性的40秒预热之后吗?我已将程序复制到VC++2012中-对于使用随机数初始化的1000个点,在这台电脑上需要约6.8毫秒。对于10k个点,需要约738毫秒。释放模式构建,无需考虑其他优化设置。(您有一个错误:如果没有比点[0]和点[1]更接近的其他点,您永远不会设置
*smallerA
*smallerB
)。避免pow而采用手工乘法:~700ms。在处理事情时,最大的区别是将
sqrt(pow(p1.X-p2.X,2)+pow(p1.Y-p2.Y,2))
更改为
sqrt(p1.X-p2.X)*(p1.X-p2.X)+(p1.Y-p2.Y)*(p1.Y-p2.Y))花费了十分之一的时间。(10000个随机点,4500毫秒下降到322毫秒)这是2015年社区edi
3 // number of points in the file. Note that there no comments in the actual file
(3.7098722472288, 4.49056397953787) // point (X,Y)
(8.90232811621332, 9.67982769279173)
(5.68254334818822, 1.71918922506136)
(6.22585901842366, 9.51660500242835)
(Point 1)
(Point 2)
(Point 3)
(Point 1)
(Point 2)
(Point 3)
x64
Optimization: Maximize Speed (/O2)
Intrinsic Functions: Yes (/Oi)
Favor Size or Speed: Favor fast code (/Ot)
Enable Fiber-Safe Optimizations: Yes (/GT)
Security Check: Disable Security Check (/GS-)
Floating point model: Fast (/fp:fast)
Everything else: default
x64
Release Mode
Optimize Code: Enabled
Check for arithmetic overflow: Disabled
.NET 4.5.2 
JRE/JDK 1.8
Default settings (if there are any)
double pow(double base, int exponent );
double pow(double base, double exponent) {
    return exp(log(base) * exponent);
}