gxqcn
发表于 2008-3-26 07:33:49
原帖由 无心人 于 2008-3-25 21:50 发表 http://images.5d6d.net/dz60/common/back.gif
总感觉你这么安排指令会存在冲突
我在写汇编时,尽量让相同指令聚集,且尽量让相邻语句无关,即减少依赖性;
但确实有许多靠经验、凭感觉的成分在组织安排指令顺序。
算法是经过仔细推敲论证过的。
不太明白你说的“冲突”是指什么?
无心人
发表于 2008-3-26 08:00:23
比如连续4个乘
每个乘均是5延迟的
最多同时执行两条
那这4个就产生执行单元冲突和指令延时的耽误
gxqcn
发表于 2008-3-26 08:07:21
ALU 指令讲究 U/V 通道的排布,
不知 SIMD 指令是否也有双通道问题?
无心人
发表于 2008-3-26 08:37:21
当然啊
你没仔细读你推荐的那些PDF么?
存在执行单元的限制啊
P4是5个执行单元,但并不是每个都能执行全部指令
每个都有固定的执行指令集合
能否并行是和执行单元数量有关的
指令在进入流水线解码就绪后是在一个地方集合等待
根据不同指令,要等不同执行单元空闲且操作数就绪后
才进入执行单元执行,最后在一个地方集合退出
每次执行单元的延迟数是不同的,虽然大部分都标1周期指令
但执行周期是不同的啊
无心人
发表于 2008-3-26 08:39:34
一个函数如果指令数少
也许在解码后,可填满指令的cache
就是能保证执行前并不存在延时
但并不能保证执行冲突
1、执行单元限制
2、指令依赖
3、某些具体限制,比如AGI等
liangbch
发表于 2008-3-26 21:58:52
写了一个 使用 MMX 寄存器,SSE2 乘 和 加 指令的版本,速度大幅领先于 现有的所有版本。
先贴上测试结果(PIV 2.6G 768M RAM),为了避免将函数调用放在后面容易占优势的嫌疑,将对该函数的调用放在前面。
Test function: UInt128x128To256_ANSI_C32(..) 10000000 times...
Elapsed time: 2035.286 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_76F(..) 10000000 times...
Elapsed time: 395.657 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_40F(..) 10000000 times...
Elapsed time: 1027.907 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_42F(..) 10000000 times...
Elapsed time: 762.286 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_54F(..) 10000000 times...
Elapsed time: 734.089 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_56F(..) 10000000 times...
Elapsed time: 607.176 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_58F(..) 10000000 times...
Elapsed time: 923.039 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
Test function: UInt128x128To256_SSE2_69F(..) 10000000 times...
Elapsed time: 529.028 ms
92F84935 2DD0353C A7166445 8B52280D * ADBE65FA 33EC4DD6 F39E291C 6C1A072B
= 63BF184A DE964DAC 8CD871A5 3FB4A4E7 D031F0A2 D4767BD7 A7C40427 3337152F
再贴上所有源代码(注意:该版有bug,作者已在 96# 修正!--gxqcn)_declspec(naked) /* 注意:该版有bug,作者已在 96# 修正!*/
void UInt128x128To256_SSE2_76F( UINT32 * const result,
const UINT32 * const left,
const UINT32 * const right )
// 使用MMX寄存器, 但用SSE2指令
// 指令的使用,乘法 使用 32bit * 32bit 的MMX指令,加法使用 64 bit + 64 bit 的SSE2指令
// 16次乘法,分4趟, 操作数一律在mmx 寄存器中进行
// 第1趟计算 left[] * right
// 第2趟计算 left[] * right
// 第3趟计算 left[] * right
// 第4趟计算 left[] * right
// 每趟的计算结果为 5 DWORD,最低位DWORD 直接存储到result, 前3趟的计算结果的高4个DWORD(中间结果)存储到一组寄存器
// 寄存器的使用
// 中间结果 放入 mm0,mm1,mm2,mm3
// 掩码寄存器 mm7,用来请64位寄存器的高32bit
// 进位 寄存器 和 当前乘积寄存器 为 mm4 和 mm5,轮换使用
// 乘数 寄存器 mm6
// 可以看到8个mmx寄存器全部派上用场
{
#define LEFT_REG eax
#define RIGHT_REG edx
#define RESULT_REG ecx
#define MASK_REG mm7
#define MUL_REG mm6
__asm
{
mov eax, 0xffffffff
mov RESULT_REG, dword ptr ; result
movd MASK_REG, eax
mov RIGHT_REG, dword ptr ; right
movq MUL_REG,
mov LEFT_REG,dword ptr ; left
//--------第1次乘
movd mm4,
pmuludq mm4, MUL_REG
movd , mm4
psrlq mm4, 32 //carry
//------第2次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
movq mm0, mm5
psrlq mm5, 32 //carry
pand mm0, MASK_REG //mm0,中间结果
//------第3次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm5 //64bit + 32 bit carry
movq mm1, mm4
psrlq mm4, 32 //carry
pand mm1, MASK_REG //mm1,中间结果
//------第4次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
movq mm2, mm5
psrlq mm5, 32 //carry
pand mm2, MASK_REG //mm2,中间结果
movq mm3, mm5 //mm3,中间结果
//----得到乘数
psrlq MUL_REG, 32 //得到right
//---第二趟乘---------
//--------第5次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm0 // 64bit + 32 bit 中间结果
movd , mm4
psrlq mm4, 32 //carry
//------第6次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
paddq mm5, mm1 //64bit + 32 bit 中间结果
movq mm0, mm5
psrlq mm5, 32 //carry
pand mm0, MASK_REG //mm0,中间结果
//------第7次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm5 //64bit + 32 bit carry
paddq mm4, mm2 //64bit + 32 bit 中间结果
movq mm1, mm4
psrlq mm4, 32 //carry
pand mm1, MASK_REG //mm1,中间结果
//------第8次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
paddq mm5, mm3 //64bit + 32 bit 中间结果
movq mm2, mm5
psrlq mm5, 32 //carry
pand mm2, MASK_REG //mm2,中间结果
movq mm3, mm5 //mm3,中间结果
//----得到乘数
movq MUL_REG,
//--------第9次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm0 // 64bit + 32 bit 中间结果
movd , mm4
psrlq mm4, 32 //carry
//------第10次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
paddq mm5, mm1 //64bit + 32 bit 中间结果
movq mm0, mm5
psrlq mm5, 32 //carry
pand mm0, MASK_REG //mm0,中间结果
//------第11次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm5 //64bit + 32 bit carry
paddq mm4, mm2 //64bit + 32 bit 中间结果
movq mm1, mm4
psrlq mm4, 32 //carry
pand mm1, MASK_REG //mm1,中间结果
//------第12次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
paddq mm5, mm3 //64bit + 32 bit 中间结果
movq mm2, mm5
psrlq mm5, 32 //carry
pand mm2, MASK_REG //mm2,中间结果
movq mm3, mm5 //mm3,中间结果
//----得到乘数
psrlq MUL_REG, 32 //得到right
//--------第13次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm0 // 64bit + 32 bit 中间结果
movd , mm4
psrlq mm4, 32 //carry
//------第14次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
paddq mm5, mm1 //64bit + 32 bit 中间结果
movd , mm5
psrlq mm5, 32 //carry
//------第15次乘
movd mm4,
pmuludq mm4, MUL_REG
paddq mm4, mm5 //64bit + 32 bit carry
paddq mm4, mm2 //64bit + 32 bit 中间结果
movd , mm4
psrlq mm4, 32 //carry
//------第16次乘
movd mm5,
pmuludq mm5, MUL_REG
paddq mm5, mm4 //64bit + 32 bit carry
paddq mm5, mm3 //64bit + 32 bit 中间结果
movq , mm5
psrlq mm5, 32 //carry
//------保存最高DWORD
movq , mm5 //mm3,中间结果
//emms
ret
}
}我的指令数为111条,指令数可能是所有版本中最小的。
gxqcn
发表于 2008-3-27 08:04:51
在我的 56# 中的指令数为 110 行(包含最后的那个 ret 指令),比ls的更少。:)
因为 mmx 寄存器与 fpu 寄存器物理上是共享的,
所以 ls 若将 emms 指令屏蔽掉,将导致程序后面无法正常切换到浮点单元,只不过测试程序通篇全整型,不存在这个问题罢了。
不知我这款 AMD CPU 是 MMX 优化得太好,还是对 SSE2 支持得太烂,测试结果如下:UInt128x128To256_ANSI_C32(..): 640.631 ms
UInt128x128To256_SSE2_40F(..): 516.867 ms
UInt128x128To256_SSE2_42F(..): 810.261 ms
UInt128x128To256_SSE2_54F(..): 755.762 ms
UInt128x128To256_SSE2_56F(..): 646.375 ms
UInt128x128To256_SSE2_58F(..): 639.292 ms
UInt128x128To256_SSE2_69F(..): 633.678 ms
UInt128x128To256_SSE2_76F(..): 379.350 ms
在我这台机器上,76F 替代了 40F 坐上了头把交椅:lol
现在,非常期待 Core 2 系列的测试结果,以了解CPU对指令集的优化程度和发展趋势。。。
无心人
发表于 2008-3-27 08:10:27
你厉害
谁帖Core2测试结果啊?
无心人
发表于 2008-3-27 09:34:31
P4 2.0测试结果
P4E P4D估计不会出现其他结果
验证了我说的MMX寄存器在P4上快的结论
现在差Core结果
40 1794
42 1907
54 1894
56894
58756
76642
76带不带emms均超其他函数
liangbch
发表于 2008-3-27 10:27:33
准备再写一个完全的SSE2版的,主要思路:
1.一次SSE2乘法指令计算 2个 32bit 乘以 32bit 的积
2.累加使用SSE2 128bit 加指令,但高64bit不使用
3.和76楼一样,使用4个XMM寄存器保存中间结果
4.依然需要使用掩码寄存器清除高bit,但使用 掩码清除高位的指令的使用次数大大减少,只需要3次.
5.依然是8大XMM寄存器其上阵,最大限度利用系统资源.
6.预计指令数将更少,但不知速度能否超越76楼.
页:
1
2
3
4
5
6
7
[8]
9
10
11
12
13
14
15