Encore un peu sur le mauvais test

Un jour, je suis tombé par hasard sur un code qu'un utilisateur utilisait pour surveiller la performance de la RAM dans sa machine virtuelle. Je ne vais pas reproduire ce code (il y a trop de lignes) et je vais me concentrer sur l'essentiel. Alors, le chat dans le 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/sn";
	for (int i=0;i<CNT;i++) {
		delete buffers[i];
	}
	delete buffers;
	return 0;
}

C'est simple : nous réservons de la mémoire et y écrivons un gigaoctet. Que montre ce test ?

$ ./memtest
4.06504 Go/s

Environ 4 Go/s.

Quoi?!?

Comment?!?

C'est un Core i7 (mĂȘme s'il n'est pas le plus rĂ©cent), DDR4, le processeur est presque au repos — POURQUOI?!

La réponse, comme toujours, est extraordinairement ordinaire.

L'opérateur new (tout comme la fonction malloc, au fait) ne réserve en réalité pas de mémoire. Lors de cet appel, l'allocateur consulte la liste des blocs disponibles dans le pool de mémoire, et s'il n'y en a pas, il appelle sbrk() pour augmenter le segment de données, puis renvoie à l'application un lien vers l'adresse du nouveau bloc récemment alloué.

Le problÚme est que le bloc alloué est entiÚrement virtuel. Les pages de mémoire réelles ne sont pas réservées.

Et lorsque la premiÚre accÚs à chaque page de ce segment alloué se produit, le MMU déclenche une page d'erreur, aprÚs quoi une page réelle est assignée à la page virtuelle à laquelle on accÚde.

Ainsi, en réalité, nous testons non pas la performance du bus et des modules RAM, mais celle du MMU et du VMM du systÚme d'exploitation. Et pour tester la véritable performance de la mémoire vive, nous devons simplement initialiser une fois les blocs réservés. Par exemple, comme ceci :

#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/sn";
	for (int i=0;i<CNT;i++) {
		delete buffers[i];
	}
	delete buffers;
	return 0;
}

C'est-à-dire que nous initialisons simplement les tampons réservés avec une valeur par défaut (char 0).

Vérifions :

$ ./memtest
28.5714 Go/s

C'est autre chose.

Morale — si vous avez besoin de grands tampons pour travailler rapidement, n'oubliez pas de les initialiser.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster