任何Word32
个数字都可以表示为Word8
个数字的线性组合,如下所示:
x = a + b * 2^8 + c * 2^16 + d * 2^24
换句话说,这是基座x
中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
的所有重复感到恼火,但转换是必要的,因此类型可以匹配。
提前致谢。
答案 0 :(得分: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
函数传递给 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