为什么这个代码在java中比在C ++和C#中快得多(Why is this code so faster in java than in C++ and C#)

系统教程 行业动态 更新时间:2024-06-14 17:03:54
为什么这个代码在java中比在C ++和C#中快得多(Why is this code so faster in java than in C++ and C#)

我正在做一个简单的作业,我不得不在C中开发一个软件来找到其中100个最近的两个点。

当我完成时,我很好奇,看看需要多少时间才能运行更多的点并启用完整的VC ++优化。 我尝试了10000次,耗时约8〜9秒。 然后我很好奇C#和Java会花费多少时间来做同样的事情。 不出所料,C#花了一点时间,9〜10秒; 然而,Java只花了大约400毫秒! 为什么会发生这种情况?

这是我在C,C#和Java中的代码:

C:

#include <stdio.h> #include <stdlib.h> #include <math.h> #include <Windows.h> long perfFrequency = 0; typedef struct { double X; double Y; } Point; double distance(Point p1, Point p2) { return sqrt(pow(p1.X - p2.X, 2) + pow(p1.Y - p2.Y, 2)); } double smallerDistance(Point *points, int size, Point *smallerA, Point *smallerB) { int i, j; double smaller = distance(points[0], points[1]); for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { double dist = distance(points[i], points[j]); if (dist < smaller) { smaller= dist; *smallerA = points[i]; *smallerB = points[j]; } } } return smaller; } void main() { // read size and points from file. int size; Point *points= (Point *)malloc(size * sizeof(Point)); // just to make sure everything is ready before the benchmark begins system("pause"); Point smallerA, smallerB; if (!QueryPerformanceFrequency((LARGE_INTEGER *)&perfFrequency)) printf("Couldn't query performance frequency."); long long start, end; double smaller; QueryPerformanceCounter((LARGE_INTEGER *)&start); smaller= smallerDistance(points, size, &smallerA, &smallerB); QueryPerformanceCounter((LARGE_INTEGER *)&end); printf("The smaller distance is: %lf. The coordinates of the most close points are: (%lf, %lf) and (%lf, %lf). Time taken: %lfms\n", smaller, smallerA.X, smallerA.Y, smallerB.X, smallerB.Y, (end - start) * 1000.0 / perfFrequency); }

C#:

using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace StructuredTest { struct Point { public double X; public double Y; } class Program { static double Distance(Point p1, Point p2) { return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); } static double SmallerDistance(Point[] points, int size, out Point smallerA, out Point smallerB) { int i, j; double smaller = Distance(points[0], points[1]); smallerA = default(Point); smallerB = default(Point); for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { double dist = Distance(points[i], points[j]); if (dist < smaller) { smaller = dist; smallerA = points[i]; smallerB = points[j]; } } } return smaller; } static void Main(string[] args) { // read size and points from file int size = int.Parse(file[0]); Point[] points= new Point[size]; // make sure everything is ready Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); Point smallerA, smallerB; double smaller; Stopwatch sw = new Stopwatch(); sw.Restart(); smaller = SmallerDistance(points, size, out smallerA, out smallerB); sw.Stop(); Console.WriteLine($"The smaller distance is: {smaller}. The coordinates of the most close points are: ({smallerA.X}, {smallerA.Y}) and " + $"({smallerB.X}, {smallerB.Y}). Time taken: {sw.ElapsedMilliseconds}ms."); } } }

Java的:

package structuredtest; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; class Point { public Point(double X, double Y) { this.X = X; this.Y = Y; } double X; double Y; } class Result { double distance; Point p1; Point p2; } public class StructuredTest { static double distance(Point p1, Point p2) { return Math.sqrt(Math.pow(p1.X - p2.X, 2) + Math.pow(p1.Y - p2.Y, 2)); } static Result smallerDistance(Point[] points, int size) { int i, j; double smaller = distance(points[0], points[1]); Result r = new Result(); for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { double dist = distance(points[i], points[j]); if (dist < smaller) { smaller = dist; r.p1 = points[i]; r.p2 = points[j]; } } } r.distance = smaller; return r; } public static void main(String[] args) throws IOException { // read size and points from file int size = Integer.parseInt(file[0]); Point[] points = new Point[size]; // make sure everything is ready System.out.println("Press any key to continue..."); System.in.read(); double start = System.nanoTime(), end; Result r = smallerDistance(points, size); end = System.nanoTime(); System.out.println("The smaller distance is: " + r.distance + ". The most close points are: (" + r.p1.X + "," + r.p1.Y + ") and " + r.p2.X + "," + r.p2.Y + "). Time taken: " + (end - start) / 1000000 + "ms."); } }

如果java以小幅度击败C和C#,我不会感到惊讶,但速度要快20倍?

该文件采用以下格式:

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)

有趣的事情:起初,我前面提到的10000点文件是我用来进行基准测试的,实际上只是100个随机点的另一个文件的复制粘贴。 喜欢这个:

(Point 1) (Point 2) (Point 3) (Point 1) (Point 2) (Point 3)

我认为没有必要生成10000个随机点,因为代码必须遍历所有数字,所以没有什么区别(只有更多的任务)。 但后来我决定生成10000个随机点,看看它们会如何反应:C和C#在大约相同的时间仍然运行(增加约50ms); 另一方面,Java增加了〜500ms。

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

我也尝试编译为C ++而不是C,但它没有任何区别。

我为C和C#使用VS 2015。

这些是每种语言的设置:

C:

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

C#:

x64 Release Mode Optimize Code: Enabled Check for arithmetic overflow: Disabled .NET 4.5.2

Java的:

JRE/JDK 1.8 Default settings (if there are any)

编辑:

好的,我按照以下建议重新进行了测试:

首先,我在C和C#中都使用了Result类/结构。 我在java中使用它但不在C / C#中的原因是因为java不能通过引用传递。 其次,我现在在main()函数中重复测试。 并感谢@Tony D捕捉该错误! :)

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

这次我只用了7000点(而不是10000),而且只进行了30次迭代测试,因为测试需要很长时间,而且在这里很晚。

结果没有太大变化:C#平均花费了5228ms,C 4424ms和Java 223ms。 Java仍然以20倍或更快的速度获胜。

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

Java:平均220ms

C#:平均195ms

C:平均195ms

如果我只在之前检查过:p

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

无论如何,坦率地说,我很惊讶Java编译器能够完全优化这一行代码,而C#和C ++则没有。 虽然我知道关于C#的角落案例检查和内部调用,但是我发现Java编译器能够注意到在代码中不需要任何角落检查是非常有趣的。

I was making a simple homework in which I had to develop a software in C to find the two nearest points between 100 of them.

When I finished, I was curious to see how much time it would take to run it with a lot more points and with full VC++ optimizations enabled. I tried with 10000 and it took about 8~9 seconds. Then I was curious to see how much time C# and Java would take to do the same thing. As expected, C# took a little longer, 9~10 seconds; Java, however, took only ~400 milliseconds! Why does this happen?!

This is my code in C, C# and Java:

C:

#include <stdio.h> #include <stdlib.h> #include <math.h> #include <Windows.h> long perfFrequency = 0; typedef struct { double X; double Y; } Point; double distance(Point p1, Point p2) { return sqrt(pow(p1.X - p2.X, 2) + pow(p1.Y - p2.Y, 2)); } double smallerDistance(Point *points, int size, Point *smallerA, Point *smallerB) { int i, j; double smaller = distance(points[0], points[1]); for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { double dist = distance(points[i], points[j]); if (dist < smaller) { smaller= dist; *smallerA = points[i]; *smallerB = points[j]; } } } return smaller; } void main() { // read size and points from file. int size; Point *points= (Point *)malloc(size * sizeof(Point)); // just to make sure everything is ready before the benchmark begins system("pause"); Point smallerA, smallerB; if (!QueryPerformanceFrequency((LARGE_INTEGER *)&perfFrequency)) printf("Couldn't query performance frequency."); long long start, end; double smaller; QueryPerformanceCounter((LARGE_INTEGER *)&start); smaller= smallerDistance(points, size, &smallerA, &smallerB); QueryPerformanceCounter((LARGE_INTEGER *)&end); printf("The smaller distance is: %lf. The coordinates of the most close points are: (%lf, %lf) and (%lf, %lf). Time taken: %lfms\n", smaller, smallerA.X, smallerA.Y, smallerB.X, smallerB.Y, (end - start) * 1000.0 / perfFrequency); }

C#:

using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; namespace StructuredTest { struct Point { public double X; public double Y; } class Program { static double Distance(Point p1, Point p2) { return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2)); } static double SmallerDistance(Point[] points, int size, out Point smallerA, out Point smallerB) { int i, j; double smaller = Distance(points[0], points[1]); smallerA = default(Point); smallerB = default(Point); for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { double dist = Distance(points[i], points[j]); if (dist < smaller) { smaller = dist; smallerA = points[i]; smallerB = points[j]; } } } return smaller; } static void Main(string[] args) { // read size and points from file int size = int.Parse(file[0]); Point[] points= new Point[size]; // make sure everything is ready Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); Point smallerA, smallerB; double smaller; Stopwatch sw = new Stopwatch(); sw.Restart(); smaller = SmallerDistance(points, size, out smallerA, out smallerB); sw.Stop(); Console.WriteLine($"The smaller distance is: {smaller}. The coordinates of the most close points are: ({smallerA.X}, {smallerA.Y}) and " + $"({smallerB.X}, {smallerB.Y}). Time taken: {sw.ElapsedMilliseconds}ms."); } } }

Java:

package structuredtest; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; class Point { public Point(double X, double Y) { this.X = X; this.Y = Y; } double X; double Y; } class Result { double distance; Point p1; Point p2; } public class StructuredTest { static double distance(Point p1, Point p2) { return Math.sqrt(Math.pow(p1.X - p2.X, 2) + Math.pow(p1.Y - p2.Y, 2)); } static Result smallerDistance(Point[] points, int size) { int i, j; double smaller = distance(points[0], points[1]); Result r = new Result(); for (i = 0; i < size; i++) { for (j = i + 1; j < size; j++) { double dist = distance(points[i], points[j]); if (dist < smaller) { smaller = dist; r.p1 = points[i]; r.p2 = points[j]; } } } r.distance = smaller; return r; } public static void main(String[] args) throws IOException { // read size and points from file int size = Integer.parseInt(file[0]); Point[] points = new Point[size]; // make sure everything is ready System.out.println("Press any key to continue..."); System.in.read(); double start = System.nanoTime(), end; Result r = smallerDistance(points, size); end = System.nanoTime(); System.out.println("The smaller distance is: " + r.distance + ". The most close points are: (" + r.p1.X + "," + r.p1.Y + ") and " + r.p2.X + "," + r.p2.Y + "). Time taken: " + (end - start) / 1000000 + "ms."); } }

If java had beaten both C and C# by a small margin I wouldn't be surprised, but 20 times faster?!

The file is in the following format:

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)

Funny thing: At first, the file with 10000 points that I mentioned earlier, which I was using to benchmark, was actually just a 100 times copy paste of another file with 100 random points. Like this:

(Point 1) (Point 2) (Point 3) (Point 1) (Point 2) (Point 3)

I thought that there was no need of generating 10000 random points because as the code has to run through all of the numbers anyway, it would make little difference (only more assignments). But then I decided to generate 10000 random points to see how they would react: both C and C# still ran in about the same time (~50ms increase); Java, on the other hand, had a increase of ~500ms.

Also, I think it is worth noting that java takes about 11 seconds when running inside NetBeans (even in "Run" mode, not "Debug").

And I also tried compiling as C++ instead of C, but it made no difference.

I'm using VS 2015 for both C and C#.

These are the settings for each language:

C:

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

C#:

x64 Release Mode Optimize Code: Enabled Check for arithmetic overflow: Disabled .NET 4.5.2

Java:

JRE/JDK 1.8 Default settings (if there are any)

EDIT:

Okay, I re-did the tests, following the suggestions:

First off, I used the Result class/struct in both C and C#. The reason I used it in java but not in C/C# is because java can't pass by reference. Second, I now repeat the test in the main() function. And thanks @Tony D for catching that bug! :)

I won't post the code because the changes are minor: simply implement exactly the java version in the other tests, that's what I did.

This time I tested with only 7000 Points (not 10000) and with only 30 iterations, because it is taking a long time to test and its quite late here.

The results didn't change much: C# took 5228ms in average, C 4424ms and Java 223ms. Java still wins by being 20 or more times faster.

Then I tried removing the calls to Math.Pow (simply changing for ((p1.X - p2.X) * (p1.X - p2.X)) + ((p1.Y - p2.Y) * (p1.Y - p2.Y))), then everything changed. The new results:

Java: 220ms average

C#: 195ms average

C: 195ms average

If I only checked that before :p

As I commented, I thought about doing that, but then decided that it would be better to test each compiler's ability to inline functions and optimize this kind of simple calls. However, when I obtained those strange results, I should've gone back and done this, but I got so nervous that I forgot about doing it.

Anyway, to be really honest, I'm surprised that the Java compiler was able to completely optimize that line of code while C#'s and C++'s weren't. Although I know about the corner case checks and the internal calls on C#, I find it really interesting that the Java compiler was able to notice that no corner-case checks were necessary in that code whatsoever.

最满意答案

正如这里所解释的那样, 在这里检查,在纯C中, 没有整数倍的过载,就像这样:

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用于正确目的的数学代码。 结果, 十二次平方变得更慢或类似的东西 。

原则上,将pow(x, 2)优化为x * x的唯一合理方法是让编译器识别这些反模式并为它们生成特殊代码。 发生这种情况时,您的C和C#编译器将无法执行此操作,而Java编译器可以执行此操作。 请注意,它与内联功能无关:只是内联exp(log(a) * b)不会使此代码更快。

作为一个结论,我想指出,编写数学代码的程序员不会在他的代码中写pow(x, 2) 。

As explained here and checked here, in pure C there is no overload with integer power, like this one:

double pow(double base, int exponent );

It means that when you call pow in C, it is processed in a way similar to:

double pow(double base, double exponent) { return exp(log(base) * exponent); }

Also there should be some check for the case of negative base and integer power, which is handled in special way. It is not a good idea to add conditions like if (exponent == 1.0) and if (exponent == 2.0) here, because it would slow down mathematical code that really uses pow for proper purposes. As a result, squaring becomes slower in twelve times or something like that.

In principle, the only reasonable way to optimize pow(x, 2) to x * x is to make compiler recognize such antipatterns and generate special code for them. It just happened so that your C and C# compilers with your settings are not able to do it, while Java compiler can do it. Note that it has nothing to do with inlining capabilities: just inlining exp(log(a) * b) would not make this code any faster.

As a conclusion, I'd like to note that no programmer good in writing mathematical code would ever write pow(x, 2) in his code.

更多推荐

本文发布于:2023-04-24 14:19:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/dzcp/6883dbce892057059924b125528b5ed5.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:快得多   代码   faster   java   code

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!