commit 9acf4e37bf31a5a6db4a5fc478e4538fd8c50574
parent 2a3373657b7ba5c0b137ca0781657f4110569171
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 25 Jan 2026 15:00:43 +0400
Merge branch 'impl/weigh' - comprehensive allocation benchmarks
Add allocation tracking benchmarks covering:
- Primitive encoders/decoders
- Truncated unsigned encoders (small/full values)
- Minimal signed encoder (1/2/4/8 byte outputs)
- BigSize encoder (1/3/9 byte outputs)
- TLV operations (encode/decode for varying record counts)
- All BOLT#1 message encoders/decoders
- Envelope operations
Diffstat:
| M | bench/Weight.hs | | | 339 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
1 file changed, 338 insertions(+), 1 deletion(-)
diff --git a/bench/Weight.hs b/bench/Weight.hs
@@ -3,8 +3,345 @@
module Main where
+import qualified Data.ByteString as BS
+import Data.Word (Word16, Word32, Word64)
+import Data.Int (Int8, Int16, Int32, Int64)
+import Lightning.Protocol.BOLT1
+import Lightning.Protocol.BOLT1.Codec
+import Lightning.Protocol.BOLT1.TLV (encodeTlvRecord)
import Weigh
+-- Fixtures --------------------------------------------------------------------
+
+-- Prevent constant folding with NOINLINE
+
+{-# NOINLINE w16Val #-}
+w16Val :: Word16
+w16Val = 0x1234
+
+{-# NOINLINE w32Val #-}
+w32Val :: Word32
+w32Val = 0x12345678
+
+{-# NOINLINE w64Val #-}
+w64Val :: Word64
+w64Val = 0x0102030405060708
+
+{-# NOINLINE s8Val #-}
+s8Val :: Int8
+s8Val = -42
+
+{-# NOINLINE s16Val #-}
+s16Val :: Int16
+s16Val = -1000
+
+{-# NOINLINE s32Val #-}
+s32Val :: Int32
+s32Val = -100000
+
+{-# NOINLINE s64Val #-}
+s64Val :: Int64
+s64Val = -10000000000
+
+{-# NOINLINE tu16Small #-}
+tu16Small :: Word16
+tu16Small = 0x7f
+
+{-# NOINLINE tu16Full #-}
+tu16Full :: Word16
+tu16Full = 0xffff
+
+{-# NOINLINE tu32Small #-}
+tu32Small :: Word32
+tu32Small = 0x42
+
+{-# NOINLINE tu32Full #-}
+tu32Full :: Word32
+tu32Full = 0xffffffff
+
+{-# NOINLINE tu64Small #-}
+tu64Small :: Word64
+tu64Small = 0x10
+
+{-# NOINLINE tu64Full #-}
+tu64Full :: Word64
+tu64Full = 0xffffffffffffffff
+
+{-# NOINLINE bigSizeSmall #-}
+bigSizeSmall :: Word64
+bigSizeSmall = 0xfc
+
+{-# NOINLINE bigSizeMedium #-}
+bigSizeMedium :: Word64
+bigSizeMedium = 0x1000
+
+{-# NOINLINE bigSizeLarge #-}
+bigSizeLarge :: Word64
+bigSizeLarge = 0x100000000
+
+-- Pre-encoded bytes for decoder benchmarks
+
+{-# NOINLINE u16Bytes #-}
+u16Bytes :: BS.ByteString
+u16Bytes = encodeU16 w16Val
+
+{-# NOINLINE u32Bytes #-}
+u32Bytes :: BS.ByteString
+u32Bytes = encodeU32 w32Val
+
+{-# NOINLINE u64Bytes #-}
+u64Bytes :: BS.ByteString
+u64Bytes = encodeU64 w64Val
+
+{-# NOINLINE s8Bytes #-}
+s8Bytes :: BS.ByteString
+s8Bytes = encodeS8 s8Val
+
+{-# NOINLINE s16Bytes #-}
+s16Bytes :: BS.ByteString
+s16Bytes = encodeS16 s16Val
+
+{-# NOINLINE s32Bytes #-}
+s32Bytes :: BS.ByteString
+s32Bytes = encodeS32 s32Val
+
+{-# NOINLINE s64Bytes #-}
+s64Bytes :: BS.ByteString
+s64Bytes = encodeS64 s64Val
+
+{-# NOINLINE bigSizeBytes #-}
+bigSizeBytes :: BS.ByteString
+bigSizeBytes = encodeBigSize bigSizeLarge
+
+-- TLV fixtures
+
+{-# NOINLINE tlvRecord1 #-}
+tlvRecord1 :: TlvRecord
+tlvRecord1 = TlvRecord 1 "test-value"
+
+{-# NOINLINE tlvStream1 #-}
+tlvStream1 :: TlvStream
+tlvStream1 = unsafeTlvStream [tlvRecord1]
+
+{-# NOINLINE tlvStream5 #-}
+tlvStream5 :: TlvStream
+tlvStream5 = unsafeTlvStream
+ [ TlvRecord 1 "value1"
+ , TlvRecord 3 "value3"
+ , TlvRecord 5 "value5"
+ , TlvRecord 7 "value7"
+ , TlvRecord 9 "value9"
+ ]
+
+{-# NOINLINE tlvStream20 #-}
+tlvStream20 :: TlvStream
+tlvStream20 = unsafeTlvStream
+ [ TlvRecord (2 * i + 1) (BS.replicate 10 (fromIntegral i))
+ | i <- [0..19]
+ ]
+
+{-# NOINLINE tlvStreamBytes1 #-}
+tlvStreamBytes1 :: BS.ByteString
+tlvStreamBytes1 = encodeTlvStream tlvStream1
+
+{-# NOINLINE tlvStreamBytes5 #-}
+tlvStreamBytes5 :: BS.ByteString
+tlvStreamBytes5 = encodeTlvStream tlvStream5
+
+{-# NOINLINE tlvStreamBytes20 #-}
+tlvStreamBytes20 :: BS.ByteString
+tlvStreamBytes20 = encodeTlvStream tlvStream20
+
+-- Message fixtures
+
+{-# NOINLINE minimalInit #-}
+minimalInit :: Init
+minimalInit = Init BS.empty BS.empty []
+
+{-# NOINLINE initWithFeatures #-}
+initWithFeatures :: Init
+initWithFeatures = Init "\x00\x08" "\x00\x0a\x8a" []
+
+{-# NOINLINE initWithTlvs #-}
+initWithTlvs :: Init
+initWithTlvs = Init BS.empty "\x00\x01" [InitRemoteAddr "127.0.0.1"]
+
+{-# NOINLINE errorMsg #-}
+errorMsg :: Error
+errorMsg = Error allChannels "something bad happened"
+
+{-# NOINLINE warningMsg #-}
+warningMsg :: Warning
+warningMsg = Warning allChannels "something concerning"
+
+{-# NOINLINE pingMinimal #-}
+pingMinimal :: Ping
+pingMinimal = Ping 4 BS.empty
+
+{-# NOINLINE pingWithPadding #-}
+pingWithPadding :: Ping
+pingWithPadding = Ping 4 (BS.replicate 64 0x00)
+
+{-# NOINLINE pongMsg #-}
+pongMsg :: Pong
+pongMsg = Pong (BS.replicate 4 0x00)
+
+{-# NOINLINE peerStorageMsg #-}
+peerStorageMsg :: PeerStorage
+peerStorageMsg = PeerStorage (BS.replicate 100 0xab)
+
+{-# NOINLINE peerStorageRetrievalMsg #-}
+peerStorageRetrievalMsg :: PeerStorageRetrieval
+peerStorageRetrievalMsg = PeerStorageRetrieval (BS.replicate 50 0xcd)
+
+-- Pre-encoded message bytes for decoder benchmarks
+
+{-# NOINLINE initMinimalBytes #-}
+initMinimalBytes :: BS.ByteString
+initMinimalBytes = either (const BS.empty) id (encodeInit minimalInit)
+
+{-# NOINLINE initWithTlvsBytes #-}
+initWithTlvsBytes :: BS.ByteString
+initWithTlvsBytes = either (const BS.empty) id (encodeInit initWithTlvs)
+
+{-# NOINLINE errorBytes #-}
+errorBytes :: BS.ByteString
+errorBytes = either (const BS.empty) id (encodeError errorMsg)
+
+{-# NOINLINE warningBytes #-}
+warningBytes :: BS.ByteString
+warningBytes = either (const BS.empty) id (encodeWarning warningMsg)
+
+{-# NOINLINE pingMinimalBytes #-}
+pingMinimalBytes :: BS.ByteString
+pingMinimalBytes = either (const BS.empty) id (encodePing pingMinimal)
+
+{-# NOINLINE pingWithPaddingBytes #-}
+pingWithPaddingBytes :: BS.ByteString
+pingWithPaddingBytes = either (const BS.empty) id (encodePing pingWithPadding)
+
+{-# NOINLINE pongBytes #-}
+pongBytes :: BS.ByteString
+pongBytes = either (const BS.empty) id (encodePong pongMsg)
+
+{-# NOINLINE peerStorageBytes #-}
+peerStorageBytes :: BS.ByteString
+peerStorageBytes = either (const BS.empty) id (encodePeerStorage peerStorageMsg)
+
+{-# NOINLINE peerStorageRetrievalBytes #-}
+peerStorageRetrievalBytes :: BS.ByteString
+peerStorageRetrievalBytes =
+ either (const BS.empty) id (encodePeerStorageRetrieval peerStorageRetrievalMsg)
+
+-- Envelope fixtures
+
+{-# NOINLINE initMessage #-}
+initMessage :: Message
+initMessage = MsgInitVal minimalInit
+
+{-# NOINLINE pingMessage #-}
+pingMessage :: Message
+pingMessage = MsgPingVal pingMinimal
+
+{-# NOINLINE envelopeBytes #-}
+envelopeBytes :: BS.ByteString
+envelopeBytes = either (const BS.empty) id (encodeEnvelope initMessage Nothing)
+
+-- Main ------------------------------------------------------------------------
+
main :: IO ()
main = mainWith $ do
- pure ()
+ setColumns [Case, Allocated, GCs, Max]
+
+ -- Primitive encoders --------------------------------------------------------
+
+ wgroup "Primitive Encoders" $ do
+ func "encodeU16" encodeU16 w16Val
+ func "encodeU32" encodeU32 w32Val
+ func "encodeU64" encodeU64 w64Val
+ func "encodeS8" encodeS8 s8Val
+ func "encodeS16" encodeS16 s16Val
+ func "encodeS32" encodeS32 s32Val
+ func "encodeS64" encodeS64 s64Val
+
+ wgroup "Truncated Unsigned Encoders" $ do
+ func "encodeTu16/small" encodeTu16 tu16Small
+ func "encodeTu16/full" encodeTu16 tu16Full
+ func "encodeTu32/small" encodeTu32 tu32Small
+ func "encodeTu32/full" encodeTu32 tu32Full
+ func "encodeTu64/small" encodeTu64 tu64Small
+ func "encodeTu64/full" encodeTu64 tu64Full
+
+ wgroup "Minimal Signed Encoder" $ do
+ func "encodeMinSigned/1-byte" encodeMinSigned (0 :: Int64)
+ func "encodeMinSigned/2-byte" encodeMinSigned (1000 :: Int64)
+ func "encodeMinSigned/4-byte" encodeMinSigned (100000 :: Int64)
+ func "encodeMinSigned/8-byte" encodeMinSigned s64Val
+
+ wgroup "BigSize Encoder" $ do
+ func "encodeBigSize/1-byte" encodeBigSize bigSizeSmall
+ func "encodeBigSize/3-byte" encodeBigSize bigSizeMedium
+ func "encodeBigSize/9-byte" encodeBigSize bigSizeLarge
+
+ -- Primitive decoders --------------------------------------------------------
+
+ wgroup "Primitive Decoders" $ do
+ func "decodeU16" decodeU16 u16Bytes
+ func "decodeU32" decodeU32 u32Bytes
+ func "decodeU64" decodeU64 u64Bytes
+ func "decodeS8" decodeS8 s8Bytes
+ func "decodeS16" decodeS16 s16Bytes
+ func "decodeS32" decodeS32 s32Bytes
+ func "decodeS64" decodeS64 s64Bytes
+ func "decodeBigSize" decodeBigSize bigSizeBytes
+
+ -- TLV operations ------------------------------------------------------------
+
+ wgroup "TLV Encoding" $ do
+ func "encodeTlvRecord" encodeTlvRecord tlvRecord1
+ func "encodeTlvStream/1-record" encodeTlvStream tlvStream1
+ func "encodeTlvStream/5-records" encodeTlvStream tlvStream5
+ func "encodeTlvStream/20-records" encodeTlvStream tlvStream20
+
+ wgroup "TLV Decoding" $ do
+ func "decodeTlvStreamRaw/1-record" decodeTlvStreamRaw tlvStreamBytes1
+ func "decodeTlvStreamRaw/5-records" decodeTlvStreamRaw tlvStreamBytes5
+ func "decodeTlvStreamRaw/20-records" decodeTlvStreamRaw tlvStreamBytes20
+ func "decodeTlvStream/1-record" decodeTlvStream tlvStreamBytes1
+ func "decodeTlvStream/5-records" decodeTlvStream tlvStreamBytes5
+
+ -- Message encoders ----------------------------------------------------------
+
+ wgroup "Message Encoders" $ do
+ func "encodeInit/minimal" encodeInit minimalInit
+ func "encodeInit/with-features" encodeInit initWithFeatures
+ func "encodeInit/with-tlvs" encodeInit initWithTlvs
+ func "encodeError" encodeError errorMsg
+ func "encodeWarning" encodeWarning warningMsg
+ func "encodePing/minimal" encodePing pingMinimal
+ func "encodePing/with-padding" encodePing pingWithPadding
+ func "encodePong" encodePong pongMsg
+ func "encodePeerStorage" encodePeerStorage peerStorageMsg
+ func "encodePeerStorageRetrieval" encodePeerStorageRetrieval
+ peerStorageRetrievalMsg
+
+ -- Message decoders ----------------------------------------------------------
+
+ wgroup "Message Decoders" $ do
+ func "decodeInit/minimal" decodeInit initMinimalBytes
+ func "decodeInit/with-tlvs" decodeInit initWithTlvsBytes
+ func "decodeError" decodeError errorBytes
+ func "decodeWarning" decodeWarning warningBytes
+ func "decodePing/minimal" decodePing pingMinimalBytes
+ func "decodePing/with-padding" decodePing pingWithPaddingBytes
+ func "decodePong" decodePong pongBytes
+ func "decodePeerStorage" decodePeerStorage peerStorageBytes
+ func "decodePeerStorageRetrieval" decodePeerStorageRetrieval
+ peerStorageRetrievalBytes
+
+ -- Envelope operations -------------------------------------------------------
+
+ wgroup "Envelope Operations" $ do
+ func "encodeEnvelope/init" (flip encodeEnvelope Nothing) initMessage
+ func "encodeEnvelope/ping" (flip encodeEnvelope Nothing) pingMessage
+ func "decodeEnvelope" decodeEnvelope envelopeBytes