【发布时间】:2011-12-15 04:18:36
【问题描述】:
我正在着手实现其中一位 SHA3 候选者 JH。我的算法通过了 NIST 提供的所有 KAT(已知答案测试),并且还使其成为 Crypto-API 的实例。因此,我开始研究它的性能。但是我对 Haskell 很陌生,并且真的不知道在分析时要寻找什么。
目前,我的代码始终比用 C 编写的参考实现慢,对于所有输入长度,慢 10 倍(C 代码在这里找到:http://www3.ntu.edu.sg/home/wuhj/research/jh/jh_bitslice_ref64.h)。
我的 Haskell 代码可以在这里找到:https://github.com/hakoja/SHA3/blob/master/Data/Digest/JHInternal.hs。
现在我不希望您浏览我的所有代码,而只是想要一些关于几个函数的提示。我已经运行了一些性能测试,这是 GHC 生成的(部分)性能文件:
Tue Oct 25 19:01 2011 Time and Allocation Profiling Report (Final)
main +RTS -sstderr -p -hc -RTS jh e False
total time = 6.56 secs (328 ticks @ 20 ms)
total alloc = 4,086,951,472 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
roundFunction Data.Digest.JHInternal 28.4 37.4
word128Shift Data.BigWord.Word128 14.9 19.7
blockMap Data.Digest.JHInternal 11.9 12.9
getBytes Data.Serialize.Get 6.7 2.4
unGet Data.Serialize.Get 5.5 1.3
sbox Data.Digest.JHInternal 4.0 7.4
getWord64be Data.Serialize.Get 3.7 1.6
e8 Data.Digest.JHInternal 3.7 0.0
swap4 Data.Digest.JHInternal 3.0 0.7
swap16 Data.Digest.JHInternal 3.0 0.7
swap8 Data.Digest.JHInternal 1.8 0.7
swap32 Data.Digest.JHInternal 1.8 0.7
parseBlock Data.Digest.JHInternal 1.8 1.2
swap2 Data.Digest.JHInternal 1.5 0.7
swap1 Data.Digest.JHInternal 1.5 0.7
linearTransform Data.Digest.JHInternal 1.5 8.6
shiftl_w64 Data.Serialize.Get 1.2 1.1
Detailed breakdown omitted ...
现在快速介绍一下 JH 算法:
这是一个哈希算法,由一个压缩函数 F8 组成,只要存在输入块(长度为 512 位),它就会重复。这就是 SHA 函数的运作方式。 F8 函数由 E8 函数组成,该函数应用轮函数 42 次。 round函数本身由三部分组成: 一个 sbox、一个线性变换和一个排列(在我的代码中称为交换)。
因此,大部分时间花在轮函数上是合理的。我仍然想知道如何改进这些部分。例如:blockMap 函数只是一个实用函数,将函数映射到 4 元组中的元素。那么为什么它的表现如此糟糕呢?欢迎提出任何建议,而不仅仅是针对单个功能,即您是否会进行结构更改以提高性能?
我曾尝试查看核心输出,但不幸的是,这超出了我的想象。
我还会在最后附上一些堆配置文件,以备不时之需。
编辑:
我忘了提及我的设置和构建。我在 x86_64 Arch Linux 机器上运行它,GHC 7.0.3-2(我认为),带有编译选项:
ghc --make -O2 -funbox-strict-fields
不幸的是,在通过 C 或 LLVM 编译时,在 Linux 平台上 there seems to be a bug,给了我错误:
错误:XXXX 的 .size 表达式未计算为常量
所以我还没有看到效果。
【问题讨论】:
-
Data.BigWord 来自哪个包?
-
Data.BigWord.Word128 在链接的 git repo 中。
标签: performance haskell profiling