【发布时间】:2021-11-20 01:27:54
【问题描述】:
我正在尝试测量我的内存的写入带宽,我创建了一个 8G 字符数组,并使用 128 个线程在其上调用 memset。下面是代码sn-p。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
int64_t char_num = 8000000000;
int threads = 128;
int res_num = 62500000;
uint8_t* arr;
static inline double timespec_to_sec(struct timespec t)
{
return t.tv_sec * 1.0 + t.tv_nsec / 1000000000.0;
}
void* multithread_memset(void* val) {
int thread_id = *(int*)val;
memset(arr + (res_num * thread_id), 1, res_num);
return NULL;
}
void start_parallel()
{
int* thread_id = malloc(sizeof(int) * threads);
for (int i = 0; i < threads; i++) {
thread_id[i] = i;
}
pthread_t* thread_array = malloc(sizeof(pthread_t) * threads);
for (int i = 0; i < threads; i++) {
pthread_create(&thread_array[i], NULL, multithread_memset, &thread_id[i]);
}
for (int i = 0; i < threads; i++) {
pthread_join(thread_array[i], NULL);
}
}
int main(int argc, char *argv[])
{
struct timespec before;
struct timespec after;
float time = 0;
arr = malloc(char_num);
clock_gettime(CLOCK_MONOTONIC, &before);
start_parallel();
clock_gettime(CLOCK_MONOTONIC, &after);
double before_time = timespec_to_sec(before);
double after_time = timespec_to_sec(after);
time = after_time - before_time;
printf("sequential = %10.8f\n", time);
return 0;
}
根据输出,完成所有 memset 需要 0.6 秒,据我了解,这意味着 8G/0.6 = 13G 内存写入带宽。但是,我有一个 2667 MHz DDR4,它应该有 21.3 GB/s 的带宽。我的代码或计算有什么问题吗?感谢您的帮助!
【问题讨论】:
-
您假设所有线程都在不同的 CPU 上运行,并且所有线程都受 CPU 限制。而且,您只提供了一位小数点的精度。所以 0.6 可能是 0.550 到 0.649 或 12.3 GB/s 到 14.5 GB/s 之间的任何值。因此,仅测量到小数点后会产生超过 2 GB/s 的变化。
-
一方面,
memset不会只写周期。每个缓存行中的第一个写指令必然会将该行读入缓存,因为 CPU 不知道您稍后会覆盖所有它。 -
另外,128 个线程很多,除非你有 128 个内核。在它们之间切换上下文所花费的时间可能很重要。
-
8e10 不是 8G。 8G为8*1024*1024*1024
-
如果你想防止将缓存行读入 CPU 缓存,你可能想看看non-temporal writes。您不必为此编写汇编代码。你也可以使用compiler intrinsics。
标签: c memory parallel-processing memset memory-bandwidth