I know that C++ should be much faster than Python 3 because it is a compiled language as opposed to an interpreted language.
I wrote 2 two programs that use the Monte Carlo Simulation to calculate Pi, one in Python 3 and the other in C++.
Python turned out to be approximately 16x faster than C++. As seen in the photos bellow, with a repetition value of (10,000,000), Python takes 8.5 seconds whilst C++ takes 137.4 seconds.
I'm new to C++ but I can't find posts online that explains this behavior.
According to this post C++ in general should be 10x - 100x faster than Python, which is clearly not the case with me.
Please help me understand why Python is significantly faster than C++ in my case.
My results:
Monte Carlo Simulation (Estimation of Pi) in C++
Monte Carlo Simulation (Estimation of Pi) in Python 3
Python Source Code:
from random import random
import time
import sys
class MonteCarloSimulator(object):
def __init__(self, value):
self.value = value
if sys.platform == "win32":
self.G = ''
self.R = ''
self.END = ''
else:
self.G = '\033[92m'
self.R = '\033[1;31m'
self.END = '\033[0m'
def unit_circle(self, x, y):
if (x ** 2 + y ** 2) <= 1:
return True
else:
return False
def simulate(self):
print("\nProcessing calculations with a repetition value of " + self.R +
str(self.value) + self.END + " times.")
area_of_circle = 0
area_of_square = 0
start = time.clock()
for i in range(1, self.value):
x = random()
y = random()
if self.unit_circle(x, y):
area_of_circle += 1
area_of_square += 1
pi = (area_of_circle * 4) / area_of_square
runtime = time.clock() - start
print("\tCalculated Pi = " + self.G + str(pi) + self.END +
" ({0} seconds, {1} minutes)".format(round(runtime, 10),
round(runtime / 60, 10)))
print("Estimated Num of Pi is off by", abs(pi - 3.14159265359))
def main():
values = [1000, 10000, 100000, 1000000, 10000000, 100000000,1000000000, 10000000000]
for value in values: MonteCarloSimulator(value).simulate()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\nQuitting...")
sys.exit(1)
C++ Source Code:
#include <iostream> // std library
#include <random> // random number generator
#include <ctime> // calculating runtime
#include <cmath> // absolute value function
#include "MonteCarloSimmulation.hpp" // function prototypes
using namespace std;
const double g_PI {3.141592653589793238463};
int main()
{
// repitition values
long values[5] = {1000, 10000, 100000, 1000000, 10000000};//, 100000000, 1000000000, 10000000000};
// runs the simulation with the different repetition values
for (auto value : values)
simulate(value);
cout << "\nPress return to exit";
cin.get();
return 0;
}
/**
* The actual simulation
*/
void simulate(unsigned long value)
{
// start time for calculating runtime
const clock_t startTime = clock();
// area's variables
unsigned long area_of_circle = 0;
unsigned long area_of_square = 0;
// print the repitiion value
cout << "\nProcessing calculations with a repetition value of " << value <<
" times." << endl;
for (unsigned long i = 0; i != value; i++)
{
// gets random values from 0 to 1 for (x) and (y)
float x = randomFloat();
float y = randomFloat();
// checks if (x, y) are in a unit circle, if so increment circle area
if (unit_circle(x, y))
area_of_circle++;
area_of_square++;
}
// pi = area of circle * 4 / area of square
double calculatedPi = static_cast<double>(area_of_circle * 4) / area_of_square;
float endTime = static_cast<float>(clock() - startTime) / CLOCKS_PER_SEC;
// prints the value of calculated pi
cout << "\tCalculated Value of Pi: " << calculatedPi <<
" (" << endTime << " seconds, " << endTime/60 << " minutes)" << endl;
// difference between the calc value and pi
cout << "Estimated Num of Pi is off by " << abs(calculatedPi - g_PI) << '\n';
}
/**
* returns a random number from 0 to 1
*/
float randomFloat()
{
random_device rd;
default_random_engine generator(rd()); // rd() provides a random seed
uniform_real_distribution<float> distribution(0,1);
float x = distribution(generator);
return x;
}
/**
* checks if the two input parameters are inside a unit circle
*/
bool unit_circle(float x, float y)
{
if ((x*x + y*y) <= 1)
return true;
else
return false;
}
The main problem is that you're reseeding a random number generator for each random number in your C++ code. Additionally you're not compiling with optimizations enabled (
-O3
).I moved the initialization of the random number generator outside the
randomFloat
function (equally, you could usestatic
variables inside the function):and compiled with
-O3
and now C++ is considerably faster than PythonAnother possibility could be that python and C++ code use a different random number generator. Python
random
module (C code here) uses a MT19937 Mersenne Twister random number generator that is a fast PRNG optimized specifically for numerical problems such as Monte Carlo; the algorithm ofdefault_random_engine
in C++ is implementation-defined. As pointed out by Melak47, you can force the use of MT19937 PRNG in C++ with:or
P.S., Python outperforming C++ is not unheard of; the C++ algorithms value genericity whereas the Python algorithms are often quite optimized for some use cases. See for example this question on substring matching.
Not meant as an answer to your question why python is faster, just to show that python can get event faster and neater for this problem.
To possibilities to speed things up in python:
Use numpy vectorization:
And / or numba just in time compilation:
The main cost is your randomFloat() c++ method.
building a random_device, default_random_engine and uniform_real_distribution every iteration is incredibly wasteful.
By making these static I was able to increase the speed of the c++ implementation by over a factor of 100. But you'd be better served injecting them, or wrapping this in a class and making them instance members.
Original implmentation Log
Using static random generator