Sedikit lagi tentang pengujian yang buruk

Suatu hari saya tidak sengaja menemukan kode yang digunakan pengguna untuk memantau kinerja RAM di mesin virtualnya. Saya tidak akan memberikan kode ini (ada “alas kaki” di sana) dan saya hanya akan meninggalkan yang paling penting saja. Jadi, kucingnya ada di 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;
}

Sederhana saja - alokasikan memori dan tulis satu gigabyte ke dalamnya. Dan apa yang ditunjukkan oleh tes ini?

$ ./memtest
4.06504 GB / s

Sekitar 4 GB/dtk.

Apa?!?!

Bagaimana?!?!?

Ini adalah Core i7 (walaupun bukan yang terbaru), DDR4, prosesornya hampir tidak dimuat - MENGAPA?!?!

Jawabannya, seperti biasa, sangatlah biasa.

Operator baru (seperti fungsi malloc) sebenarnya tidak mengalokasikan memori. Dengan panggilan ini, pengalokasi melihat daftar lokasi kosong di kumpulan memori, dan jika tidak ada, panggil sbrk() untuk menambah segmen data, lalu kembali ke program referensi ke alamat dari lokasi baru saja dialokasikan.

Masalahnya adalah area yang dialokasikan seluruhnya bersifat virtual. Tidak ada halaman memori nyata yang dialokasikan.

Dan ketika akses pertama ke setiap halaman dari segmen yang dialokasikan ini terjadi, MMU “menembak” kesalahan halaman, setelah itu halaman virtual yang diakses diberi halaman asli.

Oleh karena itu, sebenarnya kami tidak menguji performa modul bus dan RAM, melainkan performa MMU dan VMM sistem operasi. Dan untuk menguji kinerja RAM yang sebenarnya, kita hanya perlu menginisialisasi area yang dialokasikan satu kali. Misalnya seperti ini:

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

Artinya, kita cukup menginisialisasi buffer yang dialokasikan dengan nilai default (char 0).

Kami memeriksa:

$ ./memtest
28.5714 GB / s

Hal lain.

Pesan moral dari cerita ini - jika Anda memerlukan buffer besar untuk bekerja dengan cepat, jangan lupa untuk menginisialisasinya.

Sumber: www.habr.com

Tambah komentar