我最近在 haskell 中构建了一个用于处理 IPv4 地址的库。我编写了两个函数来呈现IPv4
地址,Text
令我惊讶的是,天真的方法优于我实际考虑的方法。这是相关的部分。首先,有 的定义IPv4
:
newtype IPv4 = IPv4 { getIPv4 :: Word32 }
接下来我们有我期望表现良好的IP地址渲染器:
toDotDecimalText :: IPv4 -> Text
toDotDecimalText = LText.toStrict . TBuilder.toLazyText . toDotDecimalBuilder
{-# INLINE toDotDecimalText #-}
toDotDecimalBuilder :: IPv4 -> TBuilder.Builder
toDotDecimalBuilder (IPv4 w) =
decimal (255 .&. shiftR w 24 )
<> dot
<> decimal (255 .&. shiftR w 16 )
<> dot
<> decimal (255 .&. shiftR w 8 )
<> dot
<> decimal (255 .&. w)
where dot = TBuilder.singleton '.'
{-# INLINE toDotDecimalBuilder #-}
最后,我们有了简单的实现:
ipv4ToTextNaive :: IPv4 -> Text
ipv4ToTextNaive i = Text.pack $ concat
[ show a
, "."
, show b
, "."
, show c
, "."
, show d
]
where (a,b,c,d) = IPv4.toOctets i
最后,这是基准套件:
main :: IO ()
main = do
let ipAddr = IPv4 1000000009
defaultMain
[ bgroup "IPv4 to Text"
[ bench "Naive" $ whnf ipv4ToTextNaive ipAddr
, bench "Current Implementation" $ whnf IPv4_Text.encode ipAddr
]
]
您可以通过克隆我链接到的存储库然后stack bench --benchmark-arguments '--output=out.html'
在项目的顶级目录中运行来尝试这一点。我得到的结果是:
benchmarking IPv4 to Text/Naive
time 391.1 ns (389.9 ns .. 392.7 ns)
1.000 R² (1.000 R² .. 1.000 R²)
mean 394.2 ns (393.1 ns .. 396.4 ns)
std dev 4.989 ns (2.990 ns .. 7.700 ns)
variance introduced by outliers: 12% (moderately inflated)
benchmarking IPv4 to Text/Current Implementation
time 467.5 ns (466.0 ns .. 469.8 ns)
1.000 R² (0.999 R² .. 1.000 R²)
mean 470.9 ns (467.8 ns .. 478.3 ns)
std dev 14.75 ns (8.245 ns .. 26.96 ns)
variance introduced by outliers: 45% (moderately inflated)
幼稚的(使用[Char]
然后在最后打包)每次都Text
击败我认为会做得更好的(使用文本)。Builder
我考虑了几种可能性。一是我误用criterion
或误解了弱头范式Text
。另一个是 aBuilder
不像我想象的那样表现。我一直认为它们就像一个对位打包更聪明的差异列表,但从定义来看,我真的不太确定我应该期待什么。