Register for your free account! | Forgot your password?

Go Back   elitepvpers > Coders Den > C/C++
You last visited: Today at 23:10

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



Performancefrage zu C# vs C++

Discussion on Performancefrage zu C# vs C++ within the C/C++ forum part of the Coders Den category.

Reply
 
Old   #1

 
Terreox's Avatar
 
elite*gold: 155
Join Date: Aug 2009
Posts: 628
Received Thanks: 153
Performancefrage zu C# vs C++

In letzter Zeit hab ich immer mal wieder ein Auge auf C++ geworfen und möchte auch in Zukunft meine Kenntnisse dorthin erweitern.
Bisher habe ich hauptsächlich C# und Java programmiert.

Eines Abends war mir langweilig und ich habe ein wenig mit C# und C++ gespielt und ein wenig die Performance verglichen, soweit es mir möglich war.
Die Idee hinter dem Test war eigentlich nur, möglichst viele möglichst kleine Packete in Form von structs zu verarbeiten.

Dazu habe ich mir zwei sehr einfache structs in C# und C++ geschrieben.
Hierbei ging es mir nur darum, ein sehr kleines Packet zu erzeugen.
Code:
//C++
struct Packet
{
    int x;
};

//C#
struct Packet
{
    public int x;
}
Nun habe ich zwei for-Schleifen genommen, die äußere für die Anzahl der Runden und die innere für die Iterationen pro Runde.

Code:
//C++
using namespace std;

srand(time(NULL));

for (int r = 0; r < rounds; r++)
{
    clock_t start = clock();

    int hits = 0;
    for (int i = 0; i < iterationsPerRound; i++)
    {
        Packet packet;
        packet.x = rand();
        if (packet.x == 0)
            hits++;
    }

    clock_t end = clock();

    cout << "\nRound #" << (r + 1) << ": " << double((end-start))/CLOCKS_PER_SEC << "s (Hits: " << hits << ")" << endl;
}

//C#
var random = new Random(0xDEAD);
var swatch = new Stopwatch();
for (var r = 0; r < rounds; r++)
{
    swatch.Restart();

    var hits = 0;
    for (var i = 0; i < iterationsPerRound; i++)
    {
        Packet packet;
        packet.x = random.Next();

        if (packet.x == 0)
            hits++;
    }

    swatch.Stop();

    Console.WriteLine("Round#{0}: {1} s ({2} Hits)", (r + 1), (double)swatch.ElapsedMilliseconds / 1000, hits);
}
Mir ist bekannt, dass es oft nicht sinnvoll ist, Code 1:1 von einer Sprache in die andere zu konvertieren, da andere Sprache anders arbeiten. Aber bei diesem Test habe ich es mal gemacht.
Ich habe den Test nun durchlaufen lassen mit 10 Runden und 10.000.000 Iterationen pro Runde.
Erwartet habe ich eigentlich, dass C++ zumindestens ein wenig schneller ist als C#, ich wurde allerdings eines besseren belehrt.
Pro Runde hat die C++ Version ca. 120ms und die C# Version ca. 100ms gebraucht. Auf jedenfall war die C# Version bisher immer schneller.

Es ist ewig her, dass ich mit C++ gearbeitet habe und wollte nun mal nachfragen, wo der Grund dafür liegt.
Ist der Test einfach nur schlecht bzw nicht geeignet, ist der C++ Code/C# Code nicht optimal oder gibt es irgendeinen anderen Grund?
Oder ist C# in diesem Szenario einfach C++ überlegen?

Update1:
Nachdem ich mich nach möglichen Performanceverbesserungen umgeschaut habe, kam ich zum Thema RNG (random number generation) und einen interessanten Beitrag von Intel (
Dies dort beschriebene Methode soll den Standardgenerator ablösen und wesentlich schneller sein.

Also hab ich mir den Code kopiert und diese Methode verwendet (sowohl bei C++ als auch C#, wobei die C# Version etwas umgeschrieben werden musste).

Das hat zumindestens den ersten Durchbruch gebracht.
Der C++ Code braucht nun nur noch ca. 15ms für eine Runde und der C# Code ganze 50ms.
Jetzt ist der Jagdtrieb erst recht geweckt
Mal schauen, was ich noch so finde.

Update2:
Aufgrund von snow's Anmerkung, dass std::cout seines Wissens nach langsam sei, habe ich die Ausgabe dahingehend verändert, dass ich die Rundenergebnisse in ein Array speicher und am Ende ausgeben lasse.
Allerdings hat diese Änderung keinen Einfluss auf die Rundenzeit, da die Ausgabe bereits in der alten Version nicht mehr mitgemessen wurde.
Terreox is offline  
Old 11/06/2014, 14:14   #2

 
snow's Avatar
 
elite*gold: 724
Join Date: Mar 2011
Posts: 10,479
Received Thanks: 3,318
Ich sehe dort kein C++, das ist C mit std::cout.

Mit dem reinen Code und deinem Ergebnis kann man fast nichts anfangen. Welcher Compiler wurde verwendet? Welche Optimierungsstufe? Zudem ist std::cout meines Wissens nach relativ langsam, vor allem wenn du bei jeder Runde aufs neue den Buffer flusht.
snow is offline  
Thanks
2 Users
Old 11/06/2014, 14:27   #3

 
Terreox's Avatar
 
elite*gold: 155
Join Date: Aug 2009
Posts: 628
Received Thanks: 153
Quote:
Originally Posted by snow View Post
Ich sehe dort kein C++, das ist C mit std::cout
Dann ist es trotzdem C++, nur halt mit einem kleinen Anteil an Funktionalität, den es nur in C++ gibt.

Quote:
Originally Posted by snow View Post
Mit dem reinen Code und deinem Ergebnis kann man fast nichts anfangen. Welcher Compiler wurde verwendet? Welche Optimierungsstufe? Zudem ist std::cout meines Wissens nach relativ langsam, vor allem wenn du bei jeder Runde aufs neue den Buffer flusht.
Ich habe zwei verschiedene genutzt.
Einmal den eingebauten in Visual Studio 2013 (falls dieser cl.exe ist, dann hab ich Version 18.00.30723) und einmal mingw-gcc 4.8.1.
Bei dem Visual Studio Projekt habe ich bei den Optimierungen nur verändert, dass der Compiler die /Ot Flag eingestellt (Favor fast code). Ansonsten hab ich am Standard nichts verändert (/02 /Oi /GL sind unter Optimierungen noch angeschaltet).
Beim Gcc hab ich nichts als Optionen angegeben.
Terreox is offline  
Old 11/06/2014, 15:12   #4
 
Hiris's Avatar
 
elite*gold: 99
Join Date: Apr 2011
Posts: 730
Received Thanks: 236
Wenn man C++ als C mit Klassen versteht ist die Annahme korrekt. Eine echte c++ Lösung sähe anders aus
Hiris is offline  
Old 11/06/2014, 15:43   #5

 
Terreox's Avatar
 
elite*gold: 155
Join Date: Aug 2009
Posts: 628
Received Thanks: 153
Quote:
Originally Posted by Hiris View Post
Wenn man C++ als C mit Klassen versteht ist die Annahme korrekt. Eine echte c++ Lösung sähe anders aus
Dann zeig doch mal eine reinrassige C++ Lösung oder sag, was du an meinem bisherigen Code ändern würdest.

Vorallem wüsste ich grad echt nicht, was man da groß ändern könnte.
Das Einzige was ich bisher geändert habe, ist memset zu std::fill von den Arrays, die ich zum Zwischenspeichern der Zeiten und Hits nehme.
Ansonsten, wenn es zu clock() und clock_t keine C++ Alternative gibt, ist der Rest völlig in Ordnung.

Ich versteh sowieso nicht, warum man vorallem bei C++ immer die neuen Sachen nehmen soll.
Solange die Dinge aus C funktionieren, keine Bugs enthalten und es keine besseren Alternativen gibt, find ich die Nutzung von C in C++ völlig legitim.

Würde mich mal interessieren.
Terreox is offline  
Old 11/06/2014, 17:05   #6
 
elite*gold: 0
Join Date: Aug 2012
Posts: 236
Received Thanks: 94
Es gäbe std::default_random_engine (in random), auto start=std::chrono::high_resolution_clock::now() (in chrono) und für das Zwischenergebnis-Array std::for_each (in algorithm), aber davon abgesehen gibt es tatsächlich sehr wenig Alternativen...
Ich weiß nicht, wie schnell diese neuen Zufallsgeneratoren sind.
Tasiro is offline  
Thanks
1 User
Old 11/06/2014, 20:10   #7




 
bloodx's Avatar
 
elite*gold: 55
Join Date: Mar 2006
Posts: 4,582
Received Thanks: 1,539
Damit liege ich bei ~48ms
Code:
#include <iostream>
#include <chrono>
#include <vector>
#include <random>

static std::vector<int> iteration(10000000);

typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<double> double_seconds;

struct Packet
{
	int x;
};

int main()
{
	int hits = 0;
	int rounds = 10;

	std::random_device rd;
	std::mt19937 mt( rd() );
	std::uniform_real_distribution<double> dist(1,10000000);

	do
	{
		Clock::time_point Start = Clock::now();

		for (auto i : iteration)
		{
			Packet packet;
			packet.x = dist(mt);
			if (!packet.x)
				hits++;
		}
		Clock::time_point End = Clock::now();

		auto ms = std::chrono::duration_cast<double_seconds>(End - Start);
		std::cout << "\nRound #" << rounds << ": " << ms.count() << "s (Hits: " << hits << ")" << std::endl;

		rounds--;
	} while (rounds);
	std::cin.get();

	return 0;
}
bloodx is offline  
Thanks
1 User
Old 11/06/2014, 20:19   #8
 
Schlüsselbein's Avatar
 
elite*gold: 0
Join Date: Feb 2013
Posts: 1,137
Received Thanks: 869
Nehme die Ausgabe aus der Schleife.
Ausserdem: Du verwendest schon bei beiden Versionen den Release build oder?

@Über mir: Einen std::vector<int> für eine einfache Schleife missbrauchen? Wozu? Und wozu die Referenz auf einen Integer in der Schleife??
Schlüsselbein is offline  
Thanks
1 User
Old 11/06/2014, 20:27   #9




 
bloodx's Avatar
 
elite*gold: 55
Join Date: Mar 2006
Posts: 4,582
Received Thanks: 1,539
Quote:
Originally Posted by Schlüsselbein View Post
Nehme die Ausgabe aus der Schleife.
Ausserdem: Du verwendest schon bei beiden Versionen den Release build oder?

@Über mir: Einen std::vector<int> für eine einfache Schleife missbrauchen? Wozu? Und wozu die Referenz auf einen Integer in der Schleife??
Wollte es nur als Beispiel mit einbringen den Vector .

Habe ich bereits berichtigt, hatte noch andere Sachen getestet. hehe
bloodx is offline  
Old 11/06/2014, 20:30   #10
 
elite*gold: 8
Join Date: Sep 2014
Posts: 625
Received Thanks: 178
Nicht vergessen beim Testen in den 'Höchstleistungsmodus' zu wechseln, falls ihr nicht in dem Modus seid.
qqdev is offline  
Old 11/06/2014, 21:55   #11

 
Terreox's Avatar
 
elite*gold: 155
Join Date: Aug 2009
Posts: 628
Received Thanks: 153
Ja also die neue Zeitmessung find ich gut, ist auch in der Ausgabe genauer.

Allerdings ist das mit dem Vector quatsch, vorallem muss du bedenken, dass du mit diesem Vector grad mal 40.000.000 Bytes im Speicher hälst
Deine Version verbraucht bei mir ~39MB und braucht pro Runde ~60ms.

Aber mit der momentanen Version von mir bin ich ganz zufrieden.
Speicherverbrauch liegt bei 532KB und jede Runde braucht ~17ms.

Code:
#include <iostream>
#include <ctime>
#include <chrono>

static unsigned int g_seed;

typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<double> double_seconds;

struct Packet
{
    int x;
};

inline void fast_srand(int seed) { g_seed = seed; }

inline int fastrand()
{
    g_seed = (214013*g_seed+2531011);
    return (g_seed>>16)&0x7FFF;
}

void Benchmark(int rounds, int iterationsPerRound)
{
    fast_srand(0xDEAD);

    double timings[rounds];
    int hits[rounds];

    std::fill(timings, timings + rounds, 0.0);
    std::fill(hits, hits + rounds, 0);

    for (int r = 0; r < rounds; r++)
    {
//      clock_t start = clock();
        std::chrono::high_resolution_clock::time_point Start = Clock::now();

        int lHits = 0;
        for (int i = 0; i < iterationsPerRound; i++)
        {
            Packet packet;
            packet.x = fastrand();
            if (!packet.x)
                lHits++;
        }

//      clock_t end = clock();
        Clock::time_point End = Clock::now();

        //timings[r] = double((end-start))/CLOCKS_PER_SEC;
        timings[r] = std::chrono::duration_cast<double_seconds>(End - Start).count();
        hits[r] = lHits;
    }

    for(int r = 0; r < rounds; r++)
        std::cout << "Round " << (r+1) << ": " << timings[r] << "s " << hits[r] << "hits." << std::endl;
}

int main()
{
    Benchmark(50, 10000000);

    std::cin.get();

    return 0;
}
Auf jedenfall wieder etwas dazu gelernt
Terreox is offline  
Old 11/07/2014, 14:24   #12
 
.Lol's Avatar
 
elite*gold: 2
Join Date: Jan 2010
Posts: 422
Received Thanks: 1,160
Ihr vergleicht hier Äpfel mit Birnen.


Laut wird in C# zur Erzeugung von Zufallszahlen mittels der Radom Klasse der "Donald E. Knuth's subtractive random number generator algorithm" verwendet.

Wohingegen C++ den Mersenne Twister 19937 generator als default Generator verwendet.
(default_random_engine ist bei mir der mt19937, ob das auch so im Standard steht müsste man nachlesen)


Die Verwendung von rand() solltet ihr aus mehreren Gründen vermeiden - Hier ein über rand().
Ich habe mir mal die Mühe gemacht und die einzelnen random Engines genauer unter die Lupe genommen


(i7-2600K, Windows7-64 )
(Ich bin mir nicht sicher ob die knuth_b der gleiche ist wie der in C#)
.Lol is offline  
Thanks
1 User
Old 11/07/2014, 14:48   #13
 
Schlüsselbein's Avatar
 
elite*gold: 0
Join Date: Feb 2013
Posts: 1,137
Received Thanks: 869
Mit welchem Code? Wie viel Durchläufe?
Ein paar tipps zum Plotten: Achsenbeschriftung besser wählen (mit so riesen Werten kann man auf den ersten Blick nicht viel anfangen), Messwerte ggf. mit Fehlerbalken auftragen und anschliessend ne Regression reinlegen.
Schlüsselbein is offline  
Old 11/10/2014, 07:31   #14
 
elite*gold: 198
Join Date: Mar 2011
Posts: 835
Received Thanks: 263
Wenn ihr Programmiersprachen vergleicht solltet ihr vielleicht nicht mit RNG arbeiten, da die Implementierung bei jeder Sprache ne andere ist. Eine Einfache for schleife mit Zeitmessung wäre da wohl sinnvoller. Nebst dem sollte man bei beiden die release Version und maximale Optimierung oder keine Optimierung verwenden.
ƬheGame is offline  
Thanks
1 User
Reply




All times are GMT +1. The time now is 23:11.


Powered by vBulletin®
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2025 elitepvpers All Rights Reserved.