Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,24 @@
"name": "Release BokaFuzzer (Fuzzing)",
"program": "${workspaceFolder:boka}/Fuzzing/.build/release/BokaFuzzer",
"preLaunchTask": "swift: Build Release BokaFuzzer (Fuzzing)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/JAMTests",
"name": "Debug JAMBenchmarks (JAMTests)",
"program": "${workspaceFolder:boka}/JAMTests/.build/debug/JAMBenchmarks",
"preLaunchTask": "swift: Build Debug JAMBenchmarks (JAMTests)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:boka}/JAMTests",
"name": "Release JAMBenchmarks (JAMTests)",
"program": "${workspaceFolder:boka}/JAMTests/.build/release/JAMBenchmarks",
"preLaunchTask": "swift: Build Release JAMBenchmarks (JAMTests)"
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ extension Accumulation {

let rightQueueItems = accumulationQueue.array[index...]
let leftQueueItems = accumulationQueue.array[0 ..< index]
var allQueueItems = rightQueueItems.flatMap { $0 } + leftQueueItems.flatMap { $0 } + newQueueItems
var allQueueItems = rightQueueItems.flatMap(\.self) + leftQueueItems.flatMap(\.self) + newQueueItems

editQueue(items: &allQueueItems, accumulatedPackages: Set(zeroPrereqReports.map(\.packageSpecification.workPackageHash)))

Expand Down Expand Up @@ -697,7 +697,7 @@ extension Accumulation {
for (service, _) in accumulateOutput.gasUsed {
if accumulateStats[service] != nil { continue }

let digests = accumulated.compactMap(\.digests).flatMap { $0 }
let digests = accumulated.compactMap(\.digests).flatMap(\.self)
let num = digests.filter { $0.serviceIndex == service }.count

if num == 0 { continue }
Expand Down
106 changes: 106 additions & 0 deletions JAMTests/Benchmarks/JAMBenchmarks/JAMBenchmarks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import Benchmark
import Blockchain
import Codec
import Foundation
import JAMTests
import Utils

@inline(__always) private func w3f(_ path: String, ext: String = "bin") -> [Testcase] {
(try? JAMBenchSupport.w3fTestcases(at: path, ext: ext)) ?? []
}

// Simple sync runner for async operations (Swift 6-safe)
private final class _UnsafeBox<T>: @unchecked Sendable { var value: T? }
@inline(__always) private func blockOn<T>(_ work: @escaping @Sendable () async -> T) -> T {
let g = DispatchGroup(); g.enter()
let box = _UnsafeBox<T>()
Task.detached { box.value = await work(); g.leave() }
g.wait()
return box.value!
}

let benchmarks: @Sendable () -> Void = {
// W3F Erasure (full): encode + reconstruct
struct ErasureCodingTestcase: Codable { let data: Data; let shards: [Data] }
let erasureCases = w3f("erasure/full")
if !erasureCases.isEmpty {
let cfg = TestVariants.full.config
let basicSize = cfg.value.erasureCodedPieceSize
let recoveryCount = cfg.value.totalNumberOfValidators
let originalCount = basicSize / 2
Benchmark("w3f.erasure.full.encode+reconstruct") { _ in
for c in erasureCases {
if let t = try? JamDecoder.decode(ErasureCodingTestcase.self, from: c.data, withConfig: cfg),
let shards = try? ErasureCoding.chunk(data: t.data, basicSize: basicSize, recoveryCount: recoveryCount)
{
let typed = shards.enumerated().map { ErasureCoding.Shard(data: $0.element, index: UInt32($0.offset)) }
_ = try? ErasureCoding.reconstruct(
shards: Array(typed.prefix(originalCount)),
basicSize: basicSize,
originalCount: originalCount,
recoveryCount: recoveryCount
)
}
}
}
}

// W3F Shuffle (JSON + compute)
struct ShuffleTestCase: Codable { let input: Int; let entropy: String; let output: [Int] }
if let data = try? JAMBenchSupport.w3fFile(at: "shuffle/shuffle_tests", ext: "json"),
let tests = try? JSONDecoder().decode([ShuffleTestCase].self, from: data),
!tests.isEmpty
{
Benchmark("w3f.shuffle") { _ in
for t in tests {
var input = Array(0 ..< t.input)
if let e = Data32(fromHexString: t.entropy) { input.shuffle(randomness: e); blackHole(input) }
}
}
}

// JamTestnet traces (full): decode + touch roots
let tracePaths = ["traces/fallback", "traces/safrole", "traces/storage", "traces/preimages"]
let traces = tracePaths.flatMap { w3f($0) }
if !traces.isEmpty {
Benchmark("w3f.jamtestnet.apply.full") { benchmark in
for _ in benchmark.scaledIterations {
for c in traces {
let tc = try! JamTestnet.decodeTestcase(c, config: TestVariants.full.config)
// Run full STF apply and touch resulting state root
let result = blockOn {
await (try? JamTestnet.runSTF(tc, config: TestVariants.full.config))
}
switch result {
case let .success(stateRef):
let root = blockOn { await stateRef.value.stateRoot }
blackHole(root)
default:
blackHole(c.description)
}
}
}
}
}

// RecentHistory STF (full): updatePartial + update
struct ReportedWorkPackage: Codable { let hash: Data32; let exportsRoot: Data32 }
struct RecentHistoryInput: Codable {
let headerHash: Data32; let parentStateRoot: Data32; let accumulateRoot: Data32; let workPackages: [ReportedWorkPackage]
}
struct RecentHistoryTestcase: Codable { let input: RecentHistoryInput; let preState: RecentHistory; let postState: RecentHistory }
let historyCases = w3f("stf/history/full")
if !historyCases.isEmpty {
let cfg = TestVariants.full.config
Benchmark("w3f.history.full.update") { _ in
for c in historyCases {
let tc = try! JamDecoder.decode(RecentHistoryTestcase.self, from: c.data, withConfig: cfg)
var state = tc.preState
state.updatePartial(parentStateRoot: tc.input.parentStateRoot)
let lookup = Dictionary(uniqueKeysWithValues: tc.input.workPackages.map { ($0.hash, $0.exportsRoot) })
state.update(headerHash: tc.input.headerHash, accumulateRoot: tc.input.accumulateRoot, lookup: lookup)
blackHole(state)
}
}
}
}
20 changes: 20 additions & 0 deletions JAMTests/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ let package = Package(
.package(path: "../Blockchain"),
.package(path: "../PolkaVM"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "6.0.0"),
.package(url: "https://github.com/ordo-one/package-benchmark", .upToNextMajor(from: "1.29.4")),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down Expand Up @@ -56,6 +57,25 @@ let package = Package(
.interoperabilityMode(.Cxx),
]
),
.executableTarget(
name: "JAMBenchmarks",
dependencies: [
.product(name: "Benchmark", package: "package-benchmark"),
"JAMTests",
"Utils",
"Codec",
],
path: "Benchmarks/JAMBenchmarks",
cxxSettings: [
.unsafeFlags(["-Wno-incomplete-umbrella"]),
],
swiftSettings: [
.interoperabilityMode(.Cxx),
],
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
],
),
],
swiftLanguageModes: [.version("6")]
)
14 changes: 14 additions & 0 deletions JAMTests/Sources/JAMTests/BenchSupport.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation

// Public helpers to expose W3F testcases for external benchmark targets.
public enum JAMBenchSupport {
// Loads W3F testcases from the embedded resources within the JAMTests module.
public static func w3fTestcases(at path: String, ext: String = "bin") throws -> [Testcase] {
try TestLoader.getTestcases(path: path, extension: ext, src: .w3f)
}

// Load a single W3F resource file (e.g. a companion JSON).
public static func w3fFile(at path: String, ext: String) throws -> Data {
try TestLoader.getFile(path: path, extension: ext, src: .w3f)
}
}
8 changes: 4 additions & 4 deletions JAMTests/Sources/JAMTests/JamTestnet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Codec
import Foundation
import Utils

public struct JamTestnetTestcase: Codable {
public struct JamTestnetTestcase: Codable, Sendable {
public var preState: TestState
public var block: Block
public var postState: TestState
Expand All @@ -18,12 +18,12 @@ extension ProtocolConfig {
}
}

public struct KeyVal: Codable {
public struct KeyVal: Codable, Sendable {
public var key: Data31
public var value: Data
}

public struct TestState: Codable {
public struct TestState: Codable, Sendable {
public var root: Data32
public var keyvals: [KeyVal]

Expand Down Expand Up @@ -64,7 +64,7 @@ public enum JamTestnet {
try JamDecoder.decode(JamTestnetTestcase.self, from: input.data, withConfig: config, allowTrailingBytes: true)
}

static func runSTF(
public static func runSTF(
_ testcase: JamTestnetTestcase,
config: ProtocolConfigRef = TestVariants.tiny.config
) async throws -> Result<StateRef, Error> {
Expand Down
Loading