2016-05-16 7 views
-1

Ich weiß, dass C++ viel schneller als Python 3 sein sollte, weil es eine kompilierte Sprache im Gegensatz zu einer interpretierten Sprache ist.Warum ist meine Python 3-Implementierung viel schneller als die, die ich in C++ geschrieben habe?

Ich schrieb 2 zwei Programme, die die Monte-Carlo-Simulation verwendenPi, einen in Python 3 und die andere in C++ zu berechnen.

Python erwies sich als ungefähr 16x schneller als C++. Wie in den folgenden Bildern zu sehen, benötigt Python mit einem Wiederholungswert von (10.000.000) 8,5 Sekunden, während C++ 137,4 Sekunden benötigt.

Ich bin neu in C++, aber ich kann keine Beiträge online finden, die dieses Verhalten erklären.

Nach this post sollte C++ im Allgemeinen 10x - 100x schneller sein als Python, was bei mir eindeutig nicht der Fall ist.

Bitte helfen Sie mir zu verstehen, warum Python in meinem Fall wesentlich schneller als C++ ist.

Meine Ergebnisse:

Monte Carlo Simulation (Estimation of Pi) in C++ Monte Carlo Simulation (Estimation of Pi) using C++

Monte Carlo Simulation (Estimation of Pi) in Python 3 Monte Carlo Simulation (Estimation of Pi) using python 3

Python-Quellcode:

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++ Quellcode:

#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; 
} 
+5

Der Quellcode ist offensichtlich erforderlich, Ich denke. – Delgan

+0

* "Bei Bedarf werde ich den Quellcode für beide Programme veröffentlichen" * - wie können wir die Ergebnisse ohne es verstehen? – Galik

+0

Es wird fast seine ganze Zeit im Zufallszahlengenerator verbringen, der in beiden Fällen kompilierter Code ist. So würde ich zuerst schauen. –

Antwort

5

Das Hauptproblem ist, dass Sie einen Zufallszahlengenerator für jede Zufallszahl in C++ Code sind Nachsaat. Außerdem kompilieren Sie nicht mit aktivierten Optimierungen (-O3).

zog ich die Initialisierung des Zufallszahlengenerators außerhalb der randomFloat Funktion (ebenso, Sie static Variablen innerhalb der Funktion nutzen könnten):

random_device rd; 
default_random_engine generator(rd()); // rd() provides a random seed 
uniform_real_distribution<float> distribution(0,1); 

float randomFloat() { 
    float x = distribution(generator); 
    return x; 
} 

und kompiliert mit -O3 und jetzt C++ ist wesentlich schneller als Python


Eine andere Möglichkeit könnte sein, dass Python- und C++ - Code einen anderen Zufallszahlengenerator verwenden. Python random module (C code here) verwendet einen MT19937 Mersenne Twister Zufallszahlengenerator, der eine schnelle PRNG ist, die speziell für numerische Probleme wie Monte Carlo optimiert wurde; Der Algorithmus von default_random_engine in C++ ist implementierungsdefiniert.Wie durch Melak47 wies darauf hin, Sie die Verwendung von MT19937 PRNG in C++ zwingen kann, mit:

mt19937 generator(rd()); 

oder

mt19937_64 generator(rd()); 

P. S., Python übertraf C++ ist nicht unbekannt; Die C++ - Algorithmen bewerten die Generizität, während die Python-Algorithmen oft für einige Anwendungsfälle optimiert sind. Siehe zum Beispiel diese Frage auf substring matching.

+1

Ich habe auch 'std :: mt19937' angewendet und' 'anstelle von' 'hier verwendet: http://coliru.stacked-crooked.com/a/5196578dcc974518 – melak47

+0

Vielen Dank, das Programm ist jetzt viel schneller . – Abdulrahman7ossam

2

Die Hauptkosten sind Ihre RandomFloat() C++ - Methode.

Der Aufbau einer random_device, default_random_engine und uniform_real_distribution bei jeder Iteration ist unglaublich verschwenderisch.

Mit diesen statischen konnte ich die Geschwindigkeit der C++ - Implementierung um mehr als einen Faktor von 100 erhöhen. Aber Sie wären besser gedient, sie zu injizieren, oder wickeln Sie dies in einer Klasse und machen sie Instanzmitglieder.

#include <iostream>      // std library 
#include <random>      // random number generator 
#include <ctime>      // calculating runtime 
#include <cmath>      // absolute value function 

using namespace std; 

const double g_PI {3.141592653589793238463}; 

void simulate(unsigned long value); 
float randomFloat(); 
bool unit_circle(float x, float y); 

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() 
{ 
    static random_device rd; 
    static default_random_engine generator(rd()); // rd() provides a random seed 
    static 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; 
} 

Ursprüngliche implmentation Log

Processing calculations with a repetition value of 1000 times. 
    Calculated Value of Pi: 3.08 (0.019227 seconds, 0.00032045 minutes) 
Estimated Num of Pi is off by 0.0615927 

Processing calculations with a repetition value of 10000 times. 
    Calculated Value of Pi: 3.124 (0.162044 seconds, 0.00270073 minutes) 
Estimated Num of Pi is off by 0.0175927 

Processing calculations with a repetition value of 100000 times. 
    Calculated Value of Pi: 3.14568 (1.72181 seconds, 0.0286968 minutes) 
Estimated Num of Pi is off by 0.00408735 

//Couldn't be bothered to wait :P 

Verwendung von statischen Zufallsgenerators

Processing calculations with a repetition value of 1000 times. 
    Calculated Value of Pi: 3.136 (0.000144 seconds, 2.4e-06 minutes) 
Estimated Num of Pi is off by 0.00559265 

Processing calculations with a repetition value of 10000 times. 
    Calculated Value of Pi: 3.1824 (0.000596 seconds, 9.93333e-06 minutes) 
Estimated Num of Pi is off by 0.0408073 

Processing calculations with a repetition value of 100000 times. 
    Calculated Value of Pi: 3.14044 (0.005889 seconds, 9.815e-05 minutes) 
Estimated Num of Pi is off by 0.00115265 

Processing calculations with a repetition value of 1000000 times. 
    Calculated Value of Pi: 3.14278 (0.058896 seconds, 0.0009816 minutes) 
Estimated Num of Pi is off by 0.00118335 

Processing calculations with a repetition value of 10000000 times. 
    Calculated Value of Pi: 3.14165 (0.589034 seconds, 0.00981723 minutes) 
Estimated Num of Pi is off by 6.09464e-05 
1

Nicht als Antwort auf Ihre Frage, warum Python schneller ist, nur um zu zeigen, dass Python Ereignisse schneller und sauberer für dieses Problem bekommen kann.

Um Möglichkeiten Dinge in Python zu beschleunigen:

Verwenden numpy Vektorisierung:

import numpy as np 

def pi(N): 
    x, y = np.random.uniform(-1, 1, size=(2, N)) 
    in_circle = np.sum(x**2 + y**2 <= 1) 
    return 4 * in_circle/N 

und/oder numba gerade rechtzeitig Kompilation:

from numba import jit 
import random 

@jit 
def pi(N): 
    in_circle = 0 
    for i in range(N): 
     x = 2 * random.random() - 1 
     y = 2 * random.random() - 1 

     if x**2 + y**2 <= 1: 
      in_circle += 1 
    return 4 * in_circle/N