2013-01-23 30 views
3

任何Word32数目可如下表达为Word8号的线性组合:性能改进作用

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

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

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的所有重复感到恼火,但转换是必要的,因此类型可以匹配。

在此先感谢。

+0

我认为一个更快但可能不太便携的pproach将使用'Word32'的'Storable'实例来访问底层的字节级表示,然后直接从中读取所有四个字节。 –

+1

@GabrielGonzalez:这可能比4'divMod's更快,但它绝对不是最佳选择。使用'可存储'意味着分配一个新的内存块,复制到它并回读。 @ ertes的解决方案将避免额外的分配和复制。 –

回答

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
  • 我已经解压该类型的字段以摆脱大多数内存分配和垃圾回收。
  • 我已经从divMod转换为shiftR。你实际上不需要模操作,所以我放弃了它。

另一种提高速度的方法是根本不经历具体的数据类型。您可能想要使用字节进行计算,因此我们跳过存储和检索它们的步骤。相反,我们通过splitWord功能的延续

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 
+2

可能值得指出的是,即使你不想一路去位移,使用“quot”比“divMod”快得多。 –

+0

根据我的基准,这是不正确的。但我知道它曾经是真的。我在GHC 7.6.1的i5上编译并使用-O2编译。 – ertes

+0

这是一个很棒的答案!谢谢。一切都很完美。我不知道这些_bit-wise_操作。它看起来正是我所需要的。也作为性能改进。然后,我尝试了带有严格未装箱字段的“数据”。它更快地完成了代码。最后,我申请了_continuation_想法并且工作得非常棒。 –