Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Java seem to be executing faster than C++ - Part 2 [closed]

Introduction

This is a follow up question to the one I asked previously: Java seems to be executing bare-bones algorithms faster than C++. Why?. Through that post, I learned a few important things:

  1. I was not using Ctrl + F5 to compile and run c++ code on Visual Studios C++ Express, and this was resulting in debuging that slowed down the code execution.
  2. Vectors are as good (if not better) than pointers at handling arrays of data.
  3. My C++ is terrible. ^_^
  4. A better test of execution time would be iteration, rather than recursion.

I tried to write a simpler program, which does not use pointers (or arrays in the Java equivalent), and which is pretty straightforward in its execution. Even then, the Java execution is faster than the C++ execution. What am I doing wrong?

Code:

Java:

 public class PerformanceTest2
 {
      public static void main(String args[])
      {
           //Number of iterations
           double iterations = 1E8;
           double temp;

           //Create the variables for timing
           double start;
           double end;
           double duration; //end - start

           //Run performance test
           System.out.println("Start");
           start = System.nanoTime();
           for(double i = 0;i < iterations;i += 1)
           {
                //Overhead and display
                temp = Math.log10(i);
                if(Math.round(temp) == temp)
                {
                     System.out.println(temp);
                }
           }
           end = System.nanoTime();
           System.out.println("End");

           //Output performance test results
           duration = (end - start) / 1E9;
           System.out.println("Duration: " + duration);
      }
 }

C++:

#include <iostream>
#include <cmath>
#include <windows.h>
using namespace std;

double round(double value)
{
return floor(0.5 + value);
}
void main()
{
//Number of iterations
double iterations = 1E8;
double temp;

//Create the variables for timing
LARGE_INTEGER start; //Starting time
LARGE_INTEGER end; //Ending time
LARGE_INTEGER freq; //Rate of time update
double duration; //end - start
QueryPerformanceFrequency(&freq); //Determinine the frequency of the performance counter (high precision system timer)

//Run performance test
cout << "Start" << endl;
QueryPerformanceCounter(&start);
for(double i = 0;i < iterations;i += 1)
{
    //Overhead and display
    temp = log10(i);
    if(round(temp) == temp)
    {
        cout << temp << endl;
    }
}
QueryPerformanceCounter(&end);
cout << "End" << endl;

//Output performance test results
duration = (double)(end.QuadPart - start.QuadPart) / (double)(freq.QuadPart);
cout << "Duration: " << duration << endl;

//Dramatic pause
system("pause");
}

Observations:

For 1E8 iterations:

C++ Execution = 6.45 s

Java Execution = 4.64 s

Update:

According to Visual Studios, my C++ commandline arguments are:

/Zi /nologo /W3 /WX- /O2 /Ob2 /Oi /Ot /Oy /GL /D "_MBCS" /Gm- /EHsc /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Release\C++.pch" /Fa"Release\" /Fo"Release\" /Fd"Release\vc100.pdb" /Gd /analyze- /errorReport:queue

Update 2:

I changed the c++ code with the new round function, and I updated the time of execution.

Update 3:

I found the answer to the problem, with thanks to Steve Townsend and Loduwijk. Upon compiling my code into assembly and evaluating it, I found that the C++ assembly was creating way more memory movements than the Java assembly. This is because my JDK was using an x64 compiler, while my Visual Studio Express C++ could not use the x64 architecture, and was thus inherently slower. So, I installed the Windows SDK 7.1, and used those compilers to compile my code (in release, using ctrl + F5). Presently the time ratios are:

C++: ~2.2 s Java: ~4.6 s

Now I can compile all my code in C++, and finally get the speed that I require for my algorithms. :)

like image 339
SpeedIsGood Avatar asked Nov 27 '22 23:11

SpeedIsGood


2 Answers

It's a safe assumption that any time you see Java outperforming C++, especially by such a huge margin, you're doing something wrong. Since this is the second question dedicated to such micro-micro-optimizations, I feel I should suggest finding a less futile hobby.

That answers your question: you are using C++ (really, your operating system) wrong. As to the implied question (how?), it's easy: endl flushes the stream, where Java continues buffering it. Replace your cout line with:

cout << temp << "\n";

You do not understand benchmarking enough to compare this kind of stuff (and by this I mean comparing a single math function). I recommend buying a book on testing and benchmarking.

like image 177
Blindy Avatar answered Dec 09 '22 14:12

Blindy


You surely don't want to time the output. Remove the output statements inside each loop and rerun, to get a better comparison of what you are actually interested in. Otherwise you are also benchmarking the output functions and your video driver. The resulting speed could actually depend on whether the console window you run in is obscured or minimized at the time of the test.

Make sure you are not running a Debug build in C++. That will be a lot slower than Release, independent of how you start up the process.

EDIT: I've reproduced this test scenario locally and cannot get the same results. With your code modified (below) to remove the output, Java takes 5.40754388 seconds.

public static void main(String args[]) { // Number of iterations 
    double iterations = 1E8;
    double temp; // Create the variables for timing
    double start;
    int matches = 0;
    double end;
    double duration;
    // end - start //Run performance test
    System.out.println("Start");
    start = System.nanoTime();
    for (double i = 0; i < iterations; i += 1) {
        // Overhead and display
        temp = Math.log10(i);
        if (Math.round(temp) == temp) {
            ++matches;
        }
    }
    end = System.nanoTime();
    System.out.println("End");
    // Output performance test results
    duration = (end - start) / 1E9;
    System.out.println("Duration: " + duration);
}

C++ code below takes 5062 ms. This is with JDK 6u21 on Windows, and VC++ 10 Express.

unsigned int count(1E8);
DWORD end;
DWORD start(::GetTickCount());
double next = 0.0;

int matches(0);
for (int i = 0; i < count; ++i)
{
    double temp = log10(double(i));
    if (temp == floor(temp + 0.5))
    {
        ++count;
    }
}

end = ::GetTickCount();
std::cout << end - start << "ms for " << 100000000 << " log10s" << std::endl;

EDIT 2: If I reinstate your logic from Java a little more precisely I get almost identical times for C++ and Java which is what I'd expect given the dependency on log10 implementation.

5157ms for 100000000 log10s

5187ms for 100000000 log10s (double loop counter)

5312ms for 100000000 log10s (double loop counter, round as fn)

like image 30
Steve Townsend Avatar answered Dec 09 '22 14:12

Steve Townsend