moonlight-triangulation-1.4.0.2: bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Lane.hs
-- | The closed comparison vocabulary and its boundary projections.
--
-- The driver executes these requests, the report orders and names them, and
-- the Haskell runner interprets their command constructors. None of those
-- boundaries authors another lane universe.
module Moonlight.Triangulation.Bench.SpadeCompare.Lane
( LaneClass (..)
, LaneKind (..)
, LaneRequest (..)
, LaneObstruction (..)
, laneKindName
, laneRequestLabel
, parseLaneRequest
, renderInventoryCsv
, renderInventoryHuman
, renderLaneSpecs
, renderLaneObstruction
, renderSnapshotSpecs
) where
import Data.List (find, intercalate)
import Text.Read (readMaybe)
data LaneClass
= ParityLane
| CliffLane
deriving stock (Eq, Show)
-- | Every benchmark command understood by the Moonlight runner. The board is
-- a closed subset of this vocabulary; the remaining constructors are focused
-- diagnostics rather than Spade comparison rows.
data LaneKind
= BulkLoadLane
| IncrementalLane
| SnapshotInsertLane
| SweepAngleCollapseLane
| DegenerateLineLane
| BatchSweepLane
| NearestLane
| CdtRecoveryLane
| ConstraintIncrementalLane
| ConstraintSplitLane
| PublicationFloorLane
| RemovalLane
| SnapshotRemovalLane
| HierarchyIncrementalLane
| HierarchyDuplicateLane
| HierarchyRemovalLane
| HierarchyRemovalOnlyLane
| HierarchyRebuildOnlyLane
| InterpolationLane
| VoronoiSweepLane
| DcelWalkLane
| AlphaPersistenceLane
| IntersectionLane
| IntersectionOutsideLane
| RefineLane
deriving stock (Bounded, Enum, Eq, Show)
data LaneRequest = LaneRequest
{ laneRequestKind :: !LaneKind
, laneRequestFirst :: !Int
, laneRequestSecond :: !Int
}
deriving stock (Eq, Show)
data LaneObstruction
= UnknownLane !String
| UnknownLaneClass !String
| MalformedWorkSize !String
| NegativeWorkSize !Int
| AlphaPersistenceRequiresSpecializedRunner
deriving stock (Eq, Show)
data LaneSpec = LaneSpec
{ laneSpecClass :: !LaneClass
, laneSpecRequest :: !LaneRequest
, laneSpecDisplay :: !String
}
data SnapshotStressSpec = SnapshotStressSpec
{ snapshotSessionRequest :: !LaneRequest
, snapshotPublicationRequest :: !LaneRequest
, snapshotDisplay :: !String
}
laneKindName :: LaneKind -> String
laneKindName = \case
BulkLoadLane -> "bulk-load"
IncrementalLane -> "incremental"
SnapshotInsertLane -> "snapshot-insert"
SweepAngleCollapseLane -> "sweep-angle-collapse"
DegenerateLineLane -> "degenerate-line"
BatchSweepLane -> "batch-sweep"
NearestLane -> "nearest"
CdtRecoveryLane -> "cdt-recovery"
ConstraintIncrementalLane -> "constraint-incremental"
ConstraintSplitLane -> "constraint-split"
PublicationFloorLane -> "publication-floor"
RemovalLane -> "removal"
SnapshotRemovalLane -> "snapshot-removal"
HierarchyIncrementalLane -> "hierarchy-incremental"
HierarchyDuplicateLane -> "hierarchy-duplicate"
HierarchyRemovalLane -> "hierarchy-removal"
HierarchyRemovalOnlyLane -> "hierarchy-removal-only"
HierarchyRebuildOnlyLane -> "hierarchy-rebuild-only"
InterpolationLane -> "interpolation"
VoronoiSweepLane -> "voronoi-sweep"
DcelWalkLane -> "dcel-walk"
AlphaPersistenceLane -> "alpha-persistence"
IntersectionLane -> "intersection"
IntersectionOutsideLane -> "intersection-outside"
RefineLane -> "refine"
laneRequestLabel :: LaneRequest -> String
laneRequestLabel request =
intercalate
"-"
[ laneKindName (laneRequestKind request)
, show (laneRequestFirst request)
, show (laneRequestSecond request)
]
parseLaneRequest :: String -> String -> String -> Either LaneObstruction LaneRequest
parseLaneRequest rawKind rawFirst rawSecond =
LaneRequest
<$> parseLaneKind rawKind
<*> parseWorkSize rawFirst
<*> parseWorkSize rawSecond
renderLaneObstruction :: LaneObstruction -> String
renderLaneObstruction = \case
UnknownLane lane -> "unknown benchmark lane: " <> lane
UnknownLaneClass laneClass -> "unknown benchmark lane class: " <> laneClass
MalformedWorkSize raw -> "benchmark work size is not an integer: " <> raw
NegativeWorkSize size -> "benchmark work size is negative: " <> show size
AlphaPersistenceRequiresSpecializedRunner ->
"alpha-persistence is owned by moonlight-triangulation-alpha-spade-referent"
renderInventoryCsv :: String
renderInventoryCsv =
unlines
( fmap renderBoardLane boardLaneSpecs
<> fmap renderSnapshotLane snapshotStressSpecs
)
where
renderBoardLane specification =
csvRow
[ "lane"
, laneClassName (laneSpecClass specification)
, laneRequestLabel (laneSpecRequest specification)
, laneSpecDisplay specification
]
renderSnapshotLane specification =
csvRow
[ "lane"
, "snapshot"
, laneRequestLabel (snapshotPublicationRequest specification)
, snapshotDisplay specification
, laneRequestLabel (snapshotSessionRequest specification)
]
renderInventoryHuman :: String
renderInventoryHuman =
concatMap renderClass [ParityLane, CliffLane]
<> "snapshot lanes ("
<> show (length snapshotStressSpecs)
<> ")\n"
<> concatMap renderSnapshot snapshotStressSpecs
where
renderClass laneClass =
let specifications = laneSpecs laneClass
in laneClassName laneClass
<> " lanes ("
<> show (length specifications)
<> ")\n"
<> concatMap
((<> "\n") . (" " <>) . laneRequestLabel . laneSpecRequest)
specifications
renderSnapshot specification =
" "
<> laneRequestLabel (snapshotPublicationRequest specification)
<> " against "
<> laneRequestLabel (snapshotSessionRequest specification)
<> "\n"
renderLaneSpecs :: String -> Either LaneObstruction String
renderLaneSpecs rawClass =
unlines . fmap (renderRequestWords . laneSpecRequest) . laneSpecs
<$> parseLaneClass rawClass
renderSnapshotSpecs :: String
renderSnapshotSpecs =
unlines
( fmap
( \specification ->
renderRequestWords (snapshotSessionRequest specification)
<> " "
<> renderRequestWords (snapshotPublicationRequest specification)
)
snapshotStressSpecs
)
parseLaneKind :: String -> Either LaneObstruction LaneKind
parseLaneKind raw =
maybe
(Left (UnknownLane raw))
Right
(find ((== raw) . laneKindName) [minBound .. maxBound])
parseLaneClass :: String -> Either LaneObstruction LaneClass
parseLaneClass = \case
"parity" -> Right ParityLane
"cliff" -> Right CliffLane
other -> Left (UnknownLaneClass other)
parseWorkSize :: String -> Either LaneObstruction Int
parseWorkSize raw =
maybe
(Left (MalformedWorkSize raw))
(\size -> if size < 0 then Left (NegativeWorkSize size) else Right size)
(readMaybe raw)
laneClassName :: LaneClass -> String
laneClassName = \case
ParityLane -> "parity"
CliffLane -> "cliff"
laneSpecs :: LaneClass -> [LaneSpec]
laneSpecs laneClass = filter ((== laneClass) . laneSpecClass) boardLaneSpecs
renderRequestWords :: LaneRequest -> String
renderRequestWords request =
unwords
[ laneKindName (laneRequestKind request)
, show (laneRequestFirst request)
, show (laneRequestSecond request)
]
csvRow :: [String] -> String
csvRow = intercalate "," . fmap csvField
csvField :: String -> String
csvField field
| any (`elem` [',', '"', '\n', '\r']) field =
'"' : concatMap escapeQuote field <> "\""
| otherwise = field
where
escapeQuote '"' = "\"\""
escapeQuote character = [character]
boardLaneSpecs :: [LaneSpec]
boardLaneSpecs =
[ parity BulkLoadLane 1000 0 "Bulk load, 1k points"
, parity BulkLoadLane 10000 0 "Bulk load, 10k"
, parity IncrementalLane 1000 0 "Incremental insert, 1k"
, parity IncrementalLane 10000 0 "Incremental insert, 10k"
, parity IncrementalLane 500000 0 "Incremental insert, 500k"
, parity NearestLane 20000 5000 "Nearest, 5k queries / 20k points"
, parity CdtRecoveryLane 8000 800 "CDT recovery, 800 / 8k"
, parity RefineLane 625 0 "Refinement, 625 Steiner"
, parity RefineLane 2500 0 "Refinement, 2,500 Steiner"
, parity RemovalLane 1000 250 "Removal, 250 / 1k"
, parity RemovalLane 10000 2500 "Removal, 2.5k / 10k"
, parity RemovalLane 100000 25000 "Removal, 25k / 100k"
, parity InterpolationLane 10000 1000 "Interpolation, 1k queries / 10k"
, parity InterpolationLane 100000 2000 "Interpolation, 2k queries / 100k"
, parity VoronoiSweepLane 1000 0 "Voronoi dual sweep, 1k sites"
, parity DcelWalkLane 2000 0 "DCEL traversal, 2k points"
, parity IntersectionLane 10000 500 "Line intersection, 500 chords / 10k"
, cliff ConstraintIncrementalLane 8000 800 "Constraint one at a time, 800 / 8k"
, cliff ConstraintSplitLane 1000 0 "Constraint split, 1k crossings"
-- This deliberately stays at 1k while its parity twin also reaches 10k.
, cliff HierarchyIncrementalLane 1000 0 "Hierarchy insert, 1k"
, cliff HierarchyDuplicateLane 10000 500 "Hierarchy duplicate insert, 500 / 10k"
, cliff HierarchyRemovalLane 10000 250 "Hierarchy removal, 250 / 10k"
, cliff SweepAngleCollapseLane 2000 0 "Sweep hull index at one angle, 2k"
, cliff DegenerateLineLane 2000 0 "Exactly collinear load, face-less locate, 2k"
, cliff IntersectionOutsideLane 2000 100 "Line intersection from outside the hull, 100 / 2k"
]
where
parity :: LaneKind -> Int -> Int -> String -> LaneSpec
parity kind first second display =
LaneSpec ParityLane (LaneRequest kind first second) display
cliff :: LaneKind -> Int -> Int -> String -> LaneSpec
cliff kind first second display =
LaneSpec CliffLane (LaneRequest kind first second) display
snapshotStressSpecs :: [SnapshotStressSpec]
snapshotStressSpecs =
[ SnapshotStressSpec
(LaneRequest IncrementalLane 1000 0)
(LaneRequest SnapshotInsertLane 1000 0)
"Insert snapshots, 1k points"
, SnapshotStressSpec
(LaneRequest RemovalLane 10000 2500)
(LaneRequest SnapshotRemovalLane 10000 2500)
"Removal snapshots, 2.5k / 10k"
]