3

任何Word32数字都可以表示为Word8数字的线性组合,如下所示:

x = a + b * 2^8 + c * 2^16 + d * 2^24

换句话说,这是x在 base 中的表示2^8。为了获得这些因素,我实现了以下功能:

word32to8 :: Word32 -> (Word8,Word8,Word8,Word8)
word32to8 n = (fromIntegral a,fromIntegral b,fromIntegral c,fromIntegral d)
  where
   (d,r1) = divMod n  (2^24)
   (c,r2) = divMod r1 (2^16)
   (b,a)  = divMod r2 (2^8)

它可以正常工作,但是由于我的程序多次使用此功能,我认为你们可以给我一个关于如何改进(如果可能)此操作的性能的想法。任何微小的改进对我来说都是好的,无论是在时间上还是空间上。对我来说,它看起来如此简单,以至于无法实现性能提升,但我仍然想问这个问题,以防万一我遗漏了什么。

顺便说一句,我对 的所有重复感到恼火fromIntegral,但转换是必要的,以便类型可以匹配。

提前致谢。

4

1 回答 1

13

通过为结果定义不同的类型、使用 GHC 扩展并改用按位运算,您可能会获得显着的性能提升:

data Split =
    Split {-# UNPACK #-} !Word8
          {-# UNPACK #-} !Word8
          {-# UNPACK #-} !Word8
          {-# UNPACK #-} !Word8

splitWord :: Word32 -> Split
splitWord x =
    Split (fromIntegral x)
          (fromIntegral (shiftR x 8))
          (fromIntegral (shiftR x 16))
          (fromIntegral (shiftR x 24))

通过使用以下改进,此代码比原始函数快四倍以上:

  • 我没有使用非严格元组类型,而是定义了一个严格类型Split
  • 我已经解压缩了该类型的字段以摆脱大多数内存分配和垃圾收集。
  • 我已经从 切换divModshiftR。你实际上不需要模运算,所以我放弃了它。

另一种提高速度的方法是根本不通过具体的数据类型。您可能希望使用字节执行计算,因此我们跳过存储和检索它们的步骤。相反,我们将splitWord函数传递给continuation

splitWord :: (Word8 -> Word8 -> Word8 -> Word8 -> r) -> Word32 -> r
splitWord k x =
    k (fromIntegral x)
      (fromIntegral (shiftR x 8))
      (fromIntegral (shiftR x 16))
      (fromIntegral (shiftR x 24))

如果您仍想保存字节,则可以将Split构造函数作为延续传递:

splitWord Split 123456

但现在您也可以只执行您想要执行的计算:

splitWord (\a b c d -> a + b + c + d) 123456
于 2013-01-23T03:35:07.277 回答