Un peu plus sur les tests incorrects

Un jour, j'ai accidentellement attiré l'attention d'un code qu'un utilisateur essayait de surveiller les performances de la RAM dans sa machine virtuelle. Je ne donnerai pas ce code (il y a un "footcloth") et je ne laisserai que l'essentiel. Donc, le chat est en 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;
}

Tout est simple - nous allouons de la mémoire et y écrivons un gigaoctet. Et que montre ce test?

$ ./memtest
4,06504 Go / s


Environ 4 Go / s.

Quoi?!?!

Comment?!?!?

Il s'agit du Core i7 (bien qu'il ne soit pas le plus récent), de la DDR4, le processeur n'est presque pas chargé - POURQUOI?!?!

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

Le nouvel opérateur (comme la fonction malloc, soit dit en passant) n'alloue pas réellement de mémoire. Avec cet appel, l'allocateur examine la liste des sections libres dans le pool de mémoire, et s'il n'y en a pas, appelle sbrk () pour agrandir le segment de données, puis renvoie au programme un lien vers l'adresse de la nouvelle section juste allouée.

Le problème est que la zone sélectionnée est entièrement virtuelle. Les pages de mémoire réelle ne sont pas allouées.

Et lorsque le premier accès à chaque page à partir de ce segment sélectionné se produit, la MMU «tire» le défaut de page, après quoi la page virtuelle à laquelle l'accès est effectué est affectée à la vraie.

Par conséquent, en fait, nous testons non pas les performances des modules de bus et de RAM, mais les performances des MMU et VMM du système d'exploitation. Et pour tester les performances réelles de la RAM, il suffit d'initialiser une fois les sections allouées. 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/s\n";
	for (int i=0;i<CNT;i++) {
		delete buffers[i];
	}
	delete buffers;
	return 0;
}

Autrement dit, nous initialisons simplement les tampons alloués avec la valeur par défaut (char 0).

Vérification:

$ ./memtest
28,5714 Go / s


Autre chose.

Moral - si vous avez besoin de gros tampons pour travailler rapidement et rapidement, n'oubliez pas de les initialiser.

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


All Articles