packages feed

ac-library-hs-1.5.3.0: benchmarks/Bench/MulMod.hs

module Bench.MulMod (benches) where

import BenchLib.MulMod.Barrett64 qualified as Barrett64
import BenchLib.MulMod.BarrettWideWord qualified as BarrettWideWord
import BenchLib.MulMod.Montgomery qualified as Montgomery
import Criterion
import Data.Bifunctor (first)
import Data.Vector.Unboxed qualified as VU
import Data.Word (Word32, Word64)
import System.Random

n :: Int
n = 10000

btWW :: BarrettWideWord.Barrett
btWW = BarrettWideWord.new64 998244353

bt64 :: Barrett64.Barrett
bt64 = Barrett64.new 998244353

mont :: Montgomery.Montgomery
mont = Montgomery.new 998244353

-- | This benchmark is almost nonsense, as the pre calculation' overhead is not considered while the
-- real use case is @powMod@. However, it's useful when optimizing the successive calculation.
benches32 :: Benchmark
benches32 =
  bgroup
    "mulMod Word32 vector"
    [ bench "barrettWideWord" $ whnf (VU.foldl' (\acc -> BarrettWideWord.mulMod btWW acc . fromIntegral) w64) nonZeroRandomVec32,
      bench "barrett" $ whnf (VU.foldl' (\acc -> Barrett64.mulMod bt64 acc . fromIntegral) w64) nonZeroRandomVec32,
      -- NOTE: It skips the last reduce.
      bench "montgomery" $ whnf (VU.foldl' (\acc -> Montgomery.mulModGenerated mont acc . Montgomery.generate mont . fromIntegral) w64) nonZeroRandomVec32,
      bench "mod" $ whnf (VU.foldl' (\acc n -> acc * n `mod` 998244353) w32) nonZeroRandomVec32,
      bench "rem" $ whnf (VU.foldl' (\acc n -> acc * n `rem` 998244353) w32) nonZeroRandomVec32
    ]
  where
    w32 :: Word32
    w32 = 1
    w64 :: Word64
    w64 = 1
    -- [1, 998244383)
    nonZeroRandomVec32 :: VU.Vector Word32
    nonZeroRandomVec32 = VU.map fromIntegral $ VU.unfoldrExactN n (first (+ 1) . genWord64R (998244383 - 2)) (mkStdGen 123456789)

-- | This benchmark is almost nonsense, as the pre calculation' overhead is not considered while the
-- real use case is @powMod@. However, it's useful when optimizing the successive calculation.
benches :: Benchmark
benches =
  bgroup
    "mulMod Word64 vector"
    [ bench "barrettWideWord" $ whnf (VU.foldl' (BarrettWideWord.mulMod btWW) w64) nonZeroRandomVec64,
      bench "barrett" $ whnf (VU.foldl' (\acc -> Barrett64.mulMod bt64 acc) w64) nonZeroRandomVec64,
      -- NOTE: It skips the last reduce.
      bench "montgomery" $ whnf (VU.foldl' (\acc -> Montgomery.mulModGenerated mont acc . Montgomery.generate mont) w64) nonZeroRandomVec64,
      bench "mod" $ whnf (VU.foldl' (\acc n -> acc * n `mod` 998244353) w64) nonZeroRandomVec64,
      bench "rem" $ whnf (VU.foldl' (\acc n -> acc * n `rem` 998244353) w64) nonZeroRandomVec64
    ]
  where
    w64 :: Word64
    w64 = 1
    -- [1, 998244383)
    nonZeroRandomVec64 :: VU.Vector Word64
    nonZeroRandomVec64 = VU.map fromIntegral $ VU.unfoldrExactN n (first (+ 1) . genWord64R (998244383 - 2)) (mkStdGen 123456789)