วันหนึ่งฉันบังเอิญเจอโค้ดที่ผู้ใช้พยายามตรวจสอบประสิทธิภาพ RAM ในเครื่องเสมือนของเขา ฉันจะไม่ให้รหัสนี้ (มี "ผ้าเช็ดเท้า" อยู่ที่นั่น) และฉันจะเหลือเฉพาะรหัสที่จำเป็นที่สุดเท่านั้น ดังนั้นแมวอยู่ในสตูดิโอ!
#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;
}
ง่ายมาก - จัดสรรหน่วยความจำและเขียนหนึ่งกิกะไบต์ลงไป และการทดสอบนี้แสดงอะไร?
$ ./memtest
4.06504 GB / s
ประมาณ 4GB/วินาที
อะไร?!?!
ยังไง?!?!?
นี่คือ Core i7 (แม้ว่าจะไม่ใช่รุ่นใหม่ล่าสุด) DDR4 โปรเซสเซอร์แทบจะไม่โหลดเลย - ทำไม?!?!
คำตอบเช่นเคยนั้นเป็นเรื่องปกติธรรมดา
ตัวดำเนินการใหม่ (เหมือนกับฟังก์ชัน malloc) ไม่ได้จัดสรรหน่วยความจำจริงๆ ด้วยการเรียกนี้ ตัวจัดสรรจะดูรายการตำแหน่งว่างในพูลหน่วยความจำ และหากไม่มี ให้เรียก sbrk() เพื่อเพิ่มเซ็กเมนต์ข้อมูล จากนั้นกลับสู่โปรแกรมโดยอ้างอิงถึงที่อยู่จากตำแหน่งใหม่เพียง ได้รับการจัดสรร
ปัญหาคือพื้นที่ที่จัดสรรนั้นเป็นเสมือนทั้งหมด ไม่มีการจัดสรรเพจหน่วยความจำจริง
และเมื่อการเข้าถึงแต่ละเพจครั้งแรกจากส่วนที่จัดสรรนี้เกิดขึ้น MMU จะ "ยิง" ข้อบกพร่องของเพจ หลังจากนั้นเพจเสมือนที่ถูกเข้าถึงจะถูกกำหนดเป็นเพจจริง
ดังนั้นในความเป็นจริง เราไม่ได้ทดสอบประสิทธิภาพของโมดูลบัสและ RAM แต่เป็นประสิทธิภาพของ MMU และ VMM ของระบบปฏิบัติการ และเพื่อที่จะทดสอบประสิทธิภาพที่แท้จริงของ RAM เราเพียงแค่ต้องเริ่มต้นพื้นที่ที่จัดสรรเพียงครั้งเดียว ตัวอย่างเช่น:
#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;
}
นั่นคือเราเพียงเริ่มต้นบัฟเฟอร์ที่จัดสรรด้วยค่าเริ่มต้น (อักขระ 0)
เราตรวจสอบ:
$ ./memtest
28.5714 GB / s
อีกสิ่งหนึ่งที่.
คติประจำใจของเรื่องราว - หากคุณต้องการบัฟเฟอร์ขนาดใหญ่เพื่อให้ทำงานได้อย่างรวดเร็ว อย่าลืมเริ่มต้นใช้งานบัฟเฟอร์เหล่านั้น
ที่มา: will.com