From e0ee97bd7f401a83cf7d13b938d0161ec2ba511d Mon Sep 17 00:00:00 2001 From: Joseph Heck Date: Thu, 10 Oct 2024 09:51:46 -0700 Subject: [PATCH] adding package-benchmark sub-project (#64) --- .codecov.yml | 3 +- Benchmarks/Benchmarks/ECSBenchmark/Base.swift | 85 ++++++++ .../ECSBenchmark/OneDimensionBenchmarks.swift | 198 ++++++++++++++++++ Benchmarks/Package.swift | 28 +++ Benchmarks/README.md | 22 ++ 5 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 Benchmarks/Benchmarks/ECSBenchmark/Base.swift create mode 100644 Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift create mode 100644 Benchmarks/Package.swift create mode 100644 Benchmarks/README.md diff --git a/.codecov.yml b/.codecov.yml index 4d717c6..1db9c06 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,6 @@ ignore: - "Tests/" + - "Benchmarks/" comment: - layout: header, changes, diff \ No newline at end of file + layout: header, changes, diff diff --git a/Benchmarks/Benchmarks/ECSBenchmark/Base.swift b/Benchmarks/Benchmarks/ECSBenchmark/Base.swift new file mode 100644 index 0000000..fe0da9d --- /dev/null +++ b/Benchmarks/Benchmarks/ECSBenchmark/Base.swift @@ -0,0 +1,85 @@ +// +// Base.swift +// FirebladeECSTests +// +// Created by Christian Treffs on 09.10.17. +// + +import FirebladeECS + +class EmptyComponent: Component {} + +class Name: Component { + var name: String + init(name: String) { + self.name = name + } +} + +class Position: Component { + var x: Int + var y: Int + init(x: Int, y: Int) { + self.x = x + self.y = y + } +} + +class Velocity: Component { + var a: Float + init(a: Float) { + self.a = a + } +} + +class Party: Component { + var partying: Bool + init(partying: Bool) { + self.partying = partying + } +} + +class Color: Component { + var r: UInt8 = 0 + var g: UInt8 = 0 + var b: UInt8 = 0 +} + +class ExampleSystem { + private let family: Family2 + + init(nexus: Nexus) { + family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: EmptyComponent.self) + } + + func update(deltaT _: Double) { + for (position, velocity) in family { + position.x *= 2 + velocity.a *= 2 + } + } +} + +final class SingleGameState: SingleComponent { + var shouldQuit: Bool = false + var playerHealth: Int = 67 +} + +func setUpNexus() -> Nexus { + let numEntities = 10000 + let nexus = Nexus() + + for i in 0 ..< numEntities { + nexus.createEntity().assign(Position(x: 1 + i, y: 2 + i), + Name(name: "myName\(i)"), + Velocity(a: 3.14), + EmptyComponent(), + Color()) + } + + precondition(nexus.numEntities == numEntities) +// precondition(nexus.numFamilies == 1) + precondition(nexus.numComponents == numEntities * 5) + + return nexus +} diff --git a/Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift b/Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift new file mode 100644 index 0000000..bceee0d --- /dev/null +++ b/Benchmarks/Benchmarks/ECSBenchmark/OneDimensionBenchmarks.swift @@ -0,0 +1,198 @@ +// swiftformat:disable preferForLoop +import Benchmark +import FirebladeECS + +// derived from FirebladeECSPerformanceTests/TypedFamilyPerformanceTests.swift in the parent project + +let benchmarks = { + Benchmark("TraitMatching") { benchmark in + let nexus = setUpNexus() + let a = nexus.createEntity() + a.assign(Position(x: 1, y: 2)) + a.assign(Name(name: "myName")) + a.assign(Velocity(a: 3.14)) + a.assign(EmptyComponent()) + + let isMatch = nexus.family(requiresAll: Position.self, Velocity.self, + excludesAll: Party.self) + + for _ in benchmark.scaledIterations { + blackHole( + isMatch.canBecomeMember(a) + ) + } + } + + Benchmark("TypedFamilyEntities") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requires: Position.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entities + .forEach { (entity: Entity) in + _ = entity + } + ) + } + } + + Benchmark("TypedFamilyOneComponent") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requires: Position.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position) in + _ = position + } + ) + } + } + + Benchmark("TypedFamilyEntityOneComponent") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requires: Position.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position) in + _ = entity + _ = position + } + ) + } + } + + Benchmark("TypedFamilyTwoComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity) in + _ = position + _ = velocity + } + ) + } + } + Benchmark("TypedFamilyEntityTwoComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity) in + _ = entity + _ = position + _ = velocity + } + ) + } + } + + Benchmark("TypedFamilyThreeComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity, name: Name) in + _ = position + _ = velocity + _ = name + } + ) + } + } + Benchmark("TypedFamilyEntityThreeComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name) in + _ = entity + _ = position + _ = velocity + _ = name + } + ) + } + } + + Benchmark("TypedFamilyFourComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity, name: Name, color: Color) in + _ = position + _ = velocity + _ = name + _ = color + } + ) + } + } + + Benchmark("TypedFamilyEntityFourComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, excludesAll: Party.self) + for _ in benchmark.scaledIterations { + blackHole( + family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name, color: Color) in + _ = entity + _ = position + _ = velocity + _ = name + _ = color + } + ) + } + } + + Benchmark("TypedFamilyFiveComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self) + + for _ in benchmark.scaledIterations { + blackHole( + family + .forEach { (position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in + _ = position + _ = velocity + _ = name + _ = color + _ = empty + } + ) + } + } + + Benchmark("TypedFamilyEntityFiveComponents") { benchmark in + let nexus = setUpNexus() + let family = nexus.family(requiresAll: Position.self, Velocity.self, Name.self, Color.self, EmptyComponent.self, excludesAll: Party.self) + + for _ in benchmark.scaledIterations { + blackHole(family + .entityAndComponents + .forEach { (entity: Entity, position: Position, velocity: Velocity, name: Name, color: Color, empty: EmptyComponent) in + _ = entity + _ = position + _ = velocity + _ = name + _ = color + _ = empty + } + ) + } + } +} diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift new file mode 100644 index 0000000..62656d1 --- /dev/null +++ b/Benchmarks/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version: 5.8 + +import PackageDescription + +let package = Package( + name: "ECSBenchmarks", + platforms: [ + .iOS(.v16), + .macOS(.v13) + ], + dependencies: [ + .package(path: "../"), + .package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.4.0")) + ], + targets: [ + .executableTarget( + name: "ECSBenchmark", + dependencies: [ + .product(name: "FirebladeECS", package: "ecs"), + .product(name: "Benchmark", package: "package-benchmark") + ], + path: "Benchmarks/ECSBenchmark", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark") + ] + ) + ] +) diff --git a/Benchmarks/README.md b/Benchmarks/README.md new file mode 100644 index 0000000..0d525f6 --- /dev/null +++ b/Benchmarks/README.md @@ -0,0 +1,22 @@ +# Benchmarks for FirebladeECS + +Originally seeded by replicating performance tests into a new form leveraging [package-benchmark](https://swiftpackageindex.com/ordo-one/package-benchmark/) [Documentation](https://swiftpackageindex.com/ordo-one/package-benchmark/main/documentation/benchmark). + +To run all the available benchmarks: + + swift package benchmark --format markdown + +For more help on the package-benchmark SwiftPM plugin: + + swift package benchmark help + +Creating a local baseline: + + swift package --allow-writing-to-package-directory benchmark baseline update dev + swift package benchmark baseline list + +Comparing to a the baseline 'alpha' + + swift package benchmark baseline compare dev + +For more details on creating and comparing baselines, read [Creating and Comparing Benchmark Baselines](https://swiftpackageindex.com/ordo-one/package-benchmark/main/documentation/benchmark/creatingandcomparingbaselines).