Ychydig mwy am brofi gwael

Un diwrnod deuthum ar draws cod yn ddamweiniol bod defnyddiwr yn ceisio monitro perfformiad RAM yn ei beiriant rhithwir. Ni fyddaf yn rhoi’r cod hwn (mae “cloth troed” yno) a byddaf yn gadael y mwyaf hanfodol yn unig. Felly, mae'r gath yn y stiwdio!

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

Mae'n syml - dyrannu cof ac ysgrifennu un gigabeit i mewn iddo. A beth mae'r prawf hwn yn ei ddangos?

$ ./memtest
4.06504 GB / s

Tua 4GB/s.

Beth?!?!

Sut?!?!?

Dyma Core i7 (er nad y mwyaf newydd), DDR4, mae'r prosesydd bron heb ei lwytho - PAM?!?!

Mae'r ateb, fel bob amser, yn anarferol o gyffredin.

Nid yw'r gweithredwr newydd (fel y swyddogaeth malloc, gyda llaw) mewn gwirionedd yn dyrannu cof. Gyda'r alwad hon, mae'r dyrannwr yn edrych ar y rhestr o leoliadau rhad ac am ddim yn y pwll cof, ac os nad oes rhai, mae'n galw sbrk() i gynyddu'r segment data, ac yna'n dychwelyd i'r rhaglen gyfeiriad at y cyfeiriad o'r lleoliad newydd yn unig dyranedig.

Y broblem yw bod yr ardal ddyranedig yn gwbl rithwir. Ni ddyrennir unrhyw dudalennau cof go iawn.

A phan fydd y mynediad cyntaf i bob tudalen o'r segment neilltuedig hwn yn digwydd, mae'r MMU yn “saethu” nam tudalen, ac ar ôl hynny rhoddir un go iawn i'r dudalen rithwir sy'n cael ei chyrchu.

Felly, mewn gwirionedd, nid ydym yn profi perfformiad y modiwlau bws a RAM, ond perfformiad yr MMU a VMM y system weithredu. Ac er mwyn profi gwir berfformiad RAM, dim ond unwaith y mae angen i ni gychwyn yr ardaloedd a ddyrannwyd. Er enghraifft fel hyn:

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

Hynny yw, yn syml rydym yn cychwyn y byfferau a ddyrannwyd gyda'r gwerth rhagosodedig (torgoch 0).

Rydym yn gwirio:

$ ./memtest
28.5714 GB / s

Peth arall.

Moesol y stori - os oes angen byfferau mawr arnoch i weithio'n gyflym, peidiwch ag anghofio eu cychwyn.

Ffynhonnell: hab.com

Ychwanegu sylw