我正在尝试学习pthreads,我一直在试验一个试图检测数组上的变化的程序。 函数array_modifier()
选择一个随机元素并切换它的值(1到0,反之亦然),然后hibernate一段时间(足够大,所以不会出现竞争条件,我知道这是不好的做法)。 change_detector()
扫描数组,当元素与其先前值不匹配且等于1时,检测到更改并使用检测延迟更新diff
数组。
当有一个change_detector()
线程( NTHREADS==1
)时,它必须扫描整个数组。 当有更multithreading时,每个线程都分配了一部分数组。 每个探测器线程只捕获其数组部分的修改,因此您需要将所有4个线程的捕获时间相加,以获得捕获所有更改的总时间。
这是代码:
#include #include #include #include #include #include #define TIME_INTERVAL 100 #define CHANGES 5000 #define UNUSED(x) ((void) x) typedef struct { unsigned int tid; } parm; static volatile unsigned int* my_array; static unsigned int* old_value; static struct timeval* time_array; static unsigned int N; static unsigned long int diff[NTHREADS] = {0}; void* array_modifier(void* args); void* change_detector(void* arg); int main(int argc, char** argv) { if (argc < 2) { exit(1); } N = (unsigned int)strtoul(argv[1], NULL, 0); my_array = calloc(N, sizeof(int)); time_array = malloc(N * sizeof(struct timeval)); old_value = calloc(N, sizeof(int)); parm* p = malloc(NTHREADS * sizeof(parm)); pthread_t generator_thread; pthread_t* detector_thread = malloc(NTHREADS * sizeof(pthread_t)); for (unsigned int i = 0; i < NTHREADS; i++) { p[i].tid = i; pthread_create(&detector_thread[i], NULL, change_detector, (void*) &p[i]); } pthread_create(&generator_thread, NULL, array_modifier, NULL); pthread_join(generator_thread, NULL); usleep(500); for (unsigned int i = 0; i < NTHREADS; i++) { pthread_cancel(detector_thread[i]); } for (unsigned int i = 0; i tid; const unsigned int start = tid * (N / NTHREADS) + (tid < N % NTHREADS ? tid : N % NTHREADS); const unsigned int end = start + (N / NTHREADS) + (tid < N % NTHREADS); unsigned int r = start; while (1) { unsigned int tmp; while ((tmp = my_array[r]) == old_value[r]) { r = (r < end - 1) ? r + 1 : start; } old_value[r] = tmp; if (tmp) { struct timeval tv; gettimeofday(&tv, NULL); // detection time in usec diff[tid] += (tv.tv_sec - time_array[r].tv_sec) * 1000000 + (tv.tv_usec - time_array[r].tv_usec); } } }
当我编译和运行时:
gcc -Wall -Wextra -O3 -DNTHREADS=1 file.c -pthread && ./a.out 100
我明白了:
665
但是当我编译并运行时:
gcc -Wall -Wextra -O3 -DNTHREADS=4 file.c -pthread && ./a.out 100
我明白了:
152 190 164 242
(总计达748)。
因此,multithreading程序的延迟更大。
我的cpu有6个核心。
简短答案您在线程之间共享内存并在线程之间共享内存很慢。
长答案您的程序使用多个线程写入my_array
,另一个线程用于从my_array
读取。 有效地, my_array
由许multithreading共享。
现在假设您在多核计算机上进行基准测试,您可能希望操作系统为每个线程分配不同的核心。
请记住,在现代处理器上写入RAM非常昂贵(数百个CPU周期)。 为了提高性能,CPU具有多级缓存。 最快的Cache是小型L1缓存。 核心可以以2-3个周期的顺序写入其L1高速缓存。 L2高速缓存可能需要20-30个周期。
现在在许多CPU架构中,每个核心都有自己的L1缓存,但L2缓存是共享的。 这意味着线程(核心)之间共享的任何数据都必须通过L2缓存,这比L1缓存慢得多。 这意味着共享内存访问往往非常慢。
最重要的是,如果您希望multithreading程序运行良好,则需要确保线程不共享内存。 共享内存很慢。
除了在线程之间共享内存时,不要依赖volatile来做正确的事情,要么使用库primefaces操作要么使用互斥锁。 这是因为如果您不知道自己在做什么,某些CPU会允许乱序读取和写入,这可能会造成奇怪的事情。
multithreading程序很少与线程数完全扩展。 在您的情况下,您使用4个螺纹测量了加速因子ca 0.9(665/748)。 那不太好。
以下是需要考虑的因素:
启动线程和分割工作的开销。 对于小型作业,启动额外线程的成本可能比实际工作大得多。 不适用于这种情况,因为开销不包括在时间测量中。
“随机”变化 。 您的线程在152和242之间变化。您应该多次运行测试并使用均值或中值。
测试的大小。 通常,您可以在更大的测试中获得更可靠的测量(更多数据)。 但是,您需要考虑如何让更多数据影响L1 / L2 / L3缓存中的缓存。 如果数据太大而无法容纳到RAM中,则需要考虑磁盘I / O. 通常,multithreading实现较慢,因为它们希望一次处理更多数据,但在极少数情况下它们可以更快,这种现象称为超线性加速 。
线程间通信引起的开销。 也许不是你的情况中的一个因素,因为你没有那么多。
资源锁定导致的开销。 通常对cpu利用率的影响很小,但可能会对实际使用的总时间产生很大影响。
硬件优化。 某些CPU根据您使用的内核数量更改时钟频率 。
测量本身的成本 。 在您的情况下,将在for
循环的25(100/4)次迭代内检测到更改。 每次迭代只需几个时钟周期。 然后你调用gettimeofday
,这可能花费数千个时钟周期。 所以你实际测量的是或多或少是调用gettimeofday
的成本。
我会增加要检查的值的数量和检查每个值的成本。 我还会考虑关闭编译器优化,因为这会导致程序执行意外操作(或完全跳过某些操作)。
以上就是c/c++开发分享为什么这个程序的multithreading版本更慢?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/544812.html