我想在 yasm
程序中使用 POSIX
线程(或简称 pthread
)库实现并行处理。
代码
这是我程序中最重要的部分。
section .data
pThreadID1 dq 0
pThreadID2 dq 0
MAX: dq 100000000
value: dd 0
section .bss
extern pthread_create
extern pthread_join
section .text
global main
main:
;create the first thread with id = pThreadID1
mov rdi, pThreadID1
mov rsi, NULL
mov rdx, thread1
mov rcx, NULL
call pthread_create
;join the 1st thread
mov rdi, qword [pThreadID1]
mov rsi, NULL
call pthread_join
;create the second thread with id = pThreadID2
mov rdi, pThreadID2
mov rsi, NULL
mov rdx, thread2
mov rcx, NULL
call pthread_create
;join the 2nd thread
mov rdi, qword [pThreadID2]
mov rsi, NULL
call pthread_join
;print value block
其中 thread1
包含循环,其中 value
递增 MAX/2
次:
global thread1
thread1:
mov rcx, qword [MAX]
shr rcx, 1
thread1.loop:
mov eax, dword [value]
inc eax
mov dword [value], eax
loop thread1.loop
ret
和thread2
类似。
注意:thread1
和 thread2
共享 变量 value
。
结果
我将上面的程序汇编编译如下:
yasm -g dwarf2 -f elf64 Parallel.asm -l Parallel.lst
gcc -g Parallel.o -lpthread -o Parallel
然后我使用 time命令以了解经过的执行时间:
time ./Parallel
我明白了
value: +100000000
real 0m0.482s
user 0m0.472s
sys 0m0.000s
问题
好的。在上面的程序中,我创建了一个线程,等待它完成,然后才创建第二个线程。不是最好的“线程”,不是吗?所以我改变了程序中的顺序如下:
;create thread1
;create thread2
;join thread1
;join thread2
我希望在这种情况下耗时会更少,但我明白了
value: +48634696
real 0m2.403s
user 0m4.772s
sys 0m0.000s
我明白为什么 value
不等于 MAX
但我不明白的是 为什么在这种情况下耗时明显更多 ?我错过了什么吗?
编辑
我决定通过为每个变量使用不同的变量来排除 thread1
和 thread2
之间的重叠,然后只添加结果。在这种情况下,“并行”顺序给出的运行时间较少(与之前的结果相比),但无论如何,大于“系列”顺序。
代码
仅显示更改
数据
现在有两个变量 --- 每个线程一个。
section .data
value1: dd 0
value2: dd 0
线程
每个线程负责增加自己的值。
global thread1
thread1:
mov rcx, qword [MAX]
shr rcx, 1
thread1.loop:
mov eax, dword [value1]
inc eax
mov dword [value1], eax
loop thread1.loop
ret
thread2
类似(将 1 替换为 2)。
得到最终结果
假设评论代表问题开头的代码部分的相应代码块,程序如下。
平行订单;create thread1
;create thread1
;join thread1
;join thread2
mov eax, dword [value]
add eax, dword [value1]
add eax, dword [value2]
mov dword [value], eax
结果
value: +100000000
Performance counter stats for './Parallel':
3078.140527 cpu-clock (msec)
1.586070821 seconds time elapsed
系列订单
;create thread1
;join thread1
;create thread2
;join thread2
mov eax, dword [value]
add eax, dword [value1]
add eax, dword [value2]
mov dword [value], eax
结果
value: +100000000
Performance counter stats for './Parallel':
508.757321 cpu-clock (msec)
0.509709406 seconds time elapsed
更新
最佳答案
同时运行两个线程的版本速度较慢,因为运行代码的两个内核将竞争包含计数器值的缓存行。缓存行将不得不在两个核心之间来回移动,每次需要 10s 个周期,并且在移回另一个核心之前只会发生一些增量。将其与单线程情况相比,在单线程情况下,增量可以每 ~5 个周期发生一次1,受存储转发延迟和慢速 loop
指令限制。
对于线程递增共享值的情况以及它们不同的其他情况都是如此。它甚至适用于后一种情况,因为值 value1
和 value2
被声明为占据内存中的连续位置,因此出现在同一缓存行中。由于一致性发生在缓存行粒度上,这种所谓的“虚假共享”效应类似于真实共享(第一种情况)。
您提到虽然真共享和假共享情况都比单线程情况慢得多,但真共享情况仍然比假共享情况更慢。我原以为,在没有测试的情况下,这两种情况在性能方面是等效的,但它们没有意义:虽然两者都遭受上述缓存行抖动,但真正的共享情况可能还遭受额外的内存顺序清除 - 尽管确切的机制尚不清楚。
关于performance - 在 x86_64 汇编程序 (yasm) 中使用 POSIX 线程库需要更多的执行时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49475380/