我正试图在T4机器上快速创建大量sha256哈希。 T4有一个'sha256'指令,允许我在一个操作码中计算一个哈希值。我创建了一个内联汇编模板来调用sha256操作码:
在我的c ++代码中:
extern "C"
{
void ProcessChunk(const char* buf, uint32_t* state);
}
pchunk.il:
.inline ProcessChunk,8
.volatile
/* copy state */
ldd [%o1],%f0 /* load 8 bytes */
ldd [%o1 + 8],%f2 /* load 8 bytes */
ldd [%o1 +16],%f4 /* load 8 bytes */
ldd [%o1 +24],%f6 /* load 8 bytes */
/* copy data */
ldd [%o0],%f8 /* load 8 bytes */
ldd [%o0+8],%f10 /* load 8 bytes */
ldd [%o0+16],%f12 /* load 8 bytes */
ldd [%o0+24],%f14 /* load 8 bytes */
ldd [%o0+32],%f16 /* load 8 bytes */
ldd [%o0+40],%f18 /* load 8 bytes */
ldd [%o0+48],%f20 /* load 8 bytes */
ldd [%o0+56],%f22 /* load 8 bytes */
sha256
nop
std %f0, [%o1]
std %f2, [%o1+8]
std %f4, [%o1+16]
std %f6, [%o1+24]
.end
事情在单线程环境中运行良好,但速度不够快。我使用openmp来并行化应用程序,以便我可以同时调用ProcessChunk。应用程序的多线程版本适用于几个线程,但是当我增加线程数(例如16个)时,我开始得到虚假结果。 ProcessChunk函数的输入都是每个线程本地的堆栈变量。我已经确认无论线程数多少都能正确生成输入。如果我将ProcessChunk放入临界区,我会得到正确的结果,但性能会显着下降(单线程表现更好)。我对问题可能是什么感到难过。 solaris线程是否可以踩到另一个线程的浮点寄存器?
我有什么想法可以调试这个吗?
此致
更新
我将代码更改为使用四倍大小(16字节)加载并保存:
.inline ProcessChunk,8
.volatile
/* copy state */
ldq [%o1], %f0
ldq [%o1 +16],%f4
/* copy data */
ldq [%o0], %f8
ldq [%o0+16],%f12
ldq [%o0+32],%f16
ldq [%o0+48],%f20
lzd %o0,%o0
nop
stq %f0, [%o1]
stq %f4, [%o1+16]
.end
乍一看这个问题似乎已经消失了。在32个线程之后性能显着下降,因此这是我坚持的数量(至少目前)并且使用当前代码我似乎得到了正确的结果。我可能只是掩盖了这个问题,所以我将进行进一步的测试。
更新2:
我找到了一些时间回到这一点,我能够从T4获得不错的结果(一分钟内有数百万个哈希)。
我所做的改变是:
我将所有内容都打包在库中,并使代码可用here
答案 0 :(得分:1)
不是Spark架构专家(我可能错了),但这是我的猜测:
您的内联汇编代码将堆栈变量加载到一组特定的浮点寄存器中,以便能够调用sha asssembly操作。
这对两个线程有什么作用?对ProcessChunk的两次调用都会尝试将不同的输入值复制到相同的CPU寄存器中。
我通常看到的方式是,asm代码中的CPU寄存器就像高级编程语言的“全局”变量一样。
您的系统有多少个核心?也许你很好,直到每个核心/一组硬件寄存器都有一个线程。但这也意味着代码的行为可能取决于线程在系统的不同内核上的调度方式。
您是否知道系统在CPU内核上调度来自同一进程的线程时的行为?我的意思是:系统是否存储未调度线程的寄存器,就像上下文切换一样?
我要运行的测试是将多个线程等于产生到N个CPU内核,然后用N + 1运行相同的测试(我的假设是每个CPU内核有一个浮点寄存器集)。