我想使用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
之间的重叠,然后只添加结果。在这种情况下" parallel"订单给出较少的经过时间(与之前的结果相比),但无论如何,大于"系列"顺序。
仅显示更改
现在有两个变量 - 每个线程一个。
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
答案 0 :(得分:2)
一次运行两个线程的版本速度较慢,因为运行代码的两个核心将在包含计数器值的缓存行上竞争。高速缓存行必须在两个内核之间来回移动,每次需要10个周期,并且在它移回另一个内核之前只会发生几个增量。将其与单线程情况进行比较,其中每5个周期 1 可以发生一次增量,受到存储转发延迟和慢value1
指令的限制。
对于线程增加共享值的情况和它们不同的其他情况都是如此。它甚至适用于后一种情况,因为声明值value2
和{{1}}占据内存中的连续位置,因此出现在同一缓存行中。由于一致性发生在缓存行粒度,这种所谓的“错误共享”#34;效果类似于真实共享(第一种情况)。
您提到虽然真假共享案例都比单线程案例慢得多,但真正的共享案例仍然比虚假共享案例慢。在没有测试的情况下,我曾经想过,这两个案例在性能方面是相同的,但它们没有意义:虽然两者都遭受上述缓存行颠簸,但真正的共享案例可能还会遭受额外的内存顺序清除 - 虽然确切的机制尚不清楚。