packages feed

moonlight-planar-1.1.0.0: bench/serialization/Main.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Main (main) where

import BenchMeasure (requireRight, timedValue)
import Control.DeepSeq (NFData, force)
import Control.Exception (evaluate)
import Control.Monad (unless)
import Data.Binary (Binary)
import Data.Bifunctor (first)
import qualified Data.ByteString.Lazy as BL
import Data.Foldable (traverse_)
import Moonlight.Planar.Internal.Paged (pagedOverlayPageCount)
import Moonlight.Planar.Internal.Representation
  ( Triangulation (..), mapVertices, mapDirectedEdges, mapUndirectedEdges, mapFaces )
import Moonlight.Planar.Internal.Types (KnownConstraintMode)
import Moonlight.Planar.Internal.Validation (validateTopology)
import Moonlight.Planar.Serialization
  ( DecodingBudget (..), decodeTriangulation, encodeTriangulation, trustedBinaryPayloadDecoders )
import Moonlight.Planar.SerializationFixtures
  ( SerializationFixture, serializationFixtures, serializationGridFixtures )
import Moonlight.Planar.SerializationV6Oracle (encodeV6Gathered)
import System.Mem (performGC)
import Test.Tasty.Bench (Benchmark, bench, bgroup, defaultMain, nf)

main :: IO ()
main = do
  collinearFixtures <- concat <$> traverse prepare [10242, 32770]
  gridFixtures <- requireRight (serializationGridFixtures 10242)
  fixtures <- evaluate (force (collinearFixtures <> [("grid/10242/" <> label, mesh) | (label, mesh) <- gridFixtures]))
  unitFixtures <-
    evaluate . force $
      [ (label <> "/unit", mapVertices (const ()) . mapDirectedEdges (const ()) . mapUndirectedEdges (const ()) . mapFaces (const ()) $ mesh)
      | (label, mesh) <- fixtures
      ]
  traverse_ report fixtures
  traverse_ report unitFixtures
  defaultMain (map benchmarkPair fixtures <> map benchmarkPair unitFixtures)
 where
  prepare :: Int -> IO [(String, SerializationFixture)]
  prepare count = do
    fixtures <- requireRight (serializationFixtures count)
    evaluate (force [(show count <> "/" <> label, mesh) | (label, mesh) <- fixtures])

  benchmarkPair
    :: forall mode vertex directed undirected face.
       ( KnownConstraintMode mode, Binary vertex, Binary directed, Binary undirected, Binary face
       , NFData vertex, NFData directed, NFData undirected, NFData face )
    => (String, Triangulation mode vertex directed undirected face)
    -> Benchmark
  benchmarkPair (label, mesh) =
    bgroup label
      [ bench "gathered-v6-oracle" (nf encodeV6Gathered mesh)
      , bench "pagewise-v6" (nf encodeTriangulation mesh)
      , bench "decode-v6"
          (nf (first show . decodeTriangulation @mode @vertex @directed @undirected @face
            (DecodingBudget 100_000_000 100_000_000) trustedBinaryPayloadDecoders)
            (encodeTriangulation mesh))
      , bench "validate-topology" (nf validateTopology mesh)
      , bench "map-vertices" (nf (mapVertices (\value -> (value, True))) mesh)
      , bench "map-faces" (nf (mapFaces (\value -> (value, True))) mesh)
      ]

  report
    :: (KnownConstraintMode mode, Binary vertex, Binary directed, Binary undirected, Binary face)
    => (String, Triangulation mode vertex directed undirected face)
    -> IO ()
  report (label, mesh) = do
    performGC
    gathered <- timedValue (label <> "/gathered-v6-oracle") (pure (encodeV6Gathered mesh))
    performGC
    pagewise <- timedValue (label <> "/pagewise-v6") (pure (encodeTriangulation mesh))
    unless (gathered == pagewise) (fail (label <> ": V6 bytes differ"))
    putStrLn
      ( label <> " bytes=" <> show (BL.length pagewise)
          <> " shared-topology-pages=" <> show (pagedOverlayPageCount (triHalfTopology mesh))
      )