Ein bisschen mehr über unsachgemäße Tests

Eines Tages fiel mir versehentlich ein Code auf, mit dem ein Benutzer versuchte, die Leistung des Arbeitsspeichers in seiner virtuellen Maschine zu überwachen. Ich werde diesen Code nicht geben (es gibt ein "Fußtuch") und nur das Wesentlichste belassen. Die Katze ist also im Studio!

#include <sys/time.h>
#include <string.h>
#include <iostream>

#define CNT 1024
#define SIZE (1024*1024)

int main() {
	struct timeval start;
	struct timeval end;
	long millis;
	double gbs;
	char ** buffers;
	buffers = new char*[CNT];
	for (int i=0;i<CNT;i++) {
		buffers[i] = new char[SIZE];
	}
	gettimeofday(&start, NULL);
	for (int i=0;i<CNT;i++) {
		memset(buffers[i], 0, SIZE);
	}
	gettimeofday(&end, NULL);
	millis = (end.tv_sec - start.tv_sec) * 1000 +
		(end.tv_usec - start.tv_usec) / 1000;
	gbs = 1000.0 / millis;
	std::cout << gbs << " GB/s\n";
	for (int i=0;i<CNT;i++) {
		delete buffers[i];
	}
	delete buffers;
	return 0;
}

Alles ist einfach - wir weisen Speicher zu und schreiben ein Gigabyte hinein. Und was zeigt dieser Test?

$ ./memtest
4.06504 GB / s


Ungefähr 4 GB / s.

Was?!?!

Wie?!?!?

Dies ist Core i7 (wenn auch nicht das neueste), DDR4, der Prozessor ist fast nicht geladen - WARUM?!?!

Die Antwort ist wie immer ungewöhnlich gewöhnlich.

Der neue Operator (wie übrigens die Malloc-Funktion) weist tatsächlich keinen Speicher zu. Bei diesem Aufruf überprüft der Allokator die Liste der freien Abschnitte im Speicherpool. Wenn keine vorhanden sind, ruft er sbrk () auf, um das Datensegment zu vergrößern, und gibt dem Programm dann einen Link zur Adresse aus dem neu ausgewählten Abschnitt zurück.

Das Problem ist, dass der ausgewählte Bereich vollständig virtuell ist. Echte Speicherseiten werden nicht zugewiesen.

Und wenn der erste Zugriff auf jede Seite aus diesem ausgewählten Segment erfolgt, "schießt" die MMU den Seitenfehler, wonach die virtuelle Seite, auf die zugegriffen wird, der realen Seite zugewiesen wird.

Daher testen wir in der Tat nicht die Leistung der Bus- und RAM-Module, sondern die Leistung der MMU und VMM des Betriebssystems. Um die tatsächliche Leistung des Arbeitsspeichers zu testen, müssen die zugewiesenen Abschnitte nur einmal initialisiert werden. Zum Beispiel so:

#include <sys/time.h>
#include <string.h>
#include <iostream>

#define CNT 1024
#define SIZE (1024*1024)

int main() {
	struct timeval start;
	struct timeval end;
	long millis;
	double gbs;
	char ** buffers;
	buffers = new char*[CNT];
	for (int i=0;i<CNT;i++) {
                // FIXED HERE!!!
		buffers[i] = new char[SIZE](); // Add brackets, &$# !!!
	}
	gettimeofday(&start, NULL);
	for (int i=0;i<CNT;i++) {
		memset(buffers[i], 0, SIZE);
	}
	gettimeofday(&end, NULL);
	millis = (end.tv_sec - start.tv_sec) * 1000 +
		(end.tv_usec - start.tv_usec) / 1000;
	gbs = 1000.0 / millis;
	std::cout << gbs << " GB/s\n";
	for (int i=0;i<CNT;i++) {
		delete buffers[i];
	}
	delete buffers;
	return 0;
}

Das heißt, wir initialisieren einfach die zugewiesenen Puffer mit dem Standardwert (char 0).

Überprüfen:

$ ./memtest
28.5714 GB / s


Eine andere Sache.

Moral - Wenn Sie große Puffer benötigen, um schnell und schnell zu arbeiten, vergessen Sie nicht, diese zu initialisieren.

Source: https://habr.com/ru/post/undefined/


All Articles