//===--- Exclusivity.swift -------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // A set of tests for measuring the enforcement overhead of memory access // exclusivity rules. // //===----------------------------------------------------------------------===// import TestsUtils public let benchmarks = [ // At -Onone // 25% swift_beginAccess // 15% tlv_get_addr // 15% swift_endAccess BenchmarkInfo( name: "ExclusivityGlobal", runFunction: run_accessGlobal, tags: [.runtime, .cpubench] ), // At -Onone // 23% swift_retain // 22% swift_release // 9% swift_beginAccess // 3% swift_endAccess BenchmarkInfo( name: "ExclusivityInMatSet", runFunction: run_accessInMatSet, tags: [.runtime, .cpubench, .unstable] ), // At -Onone // 25% swift_release // 23% swift_retain // 16% swift_beginAccess // 8% swift_endAccess BenchmarkInfo( name: "ExclusivityIndependent", runFunction: run_accessIndependent, tags: [.runtime, .cpubench] ), ] // Initially these benchmarks only measure access checks at -Onone. In // the future, access checks will also be emitted at -O. // Measure memory access checks on a trivial global. // --- public var globalCounter: Int = 0 // TODO: // - Merge begin/endAccess when no calls intervene (~2x speedup). // - Move Swift runtime into the OS (~2x speedup). // - Whole module analysis can remove exclusivity checks (> 10x speedup now, 4x speedup with runtime in OS). // (The global's "public" qualifier should make the benchmark immune to this optimization.) @inline(never) public func run_accessGlobal(_ n: Int) { globalCounter = 0 for _ in 1...10000*n { globalCounter += 1 } check(globalCounter == 10000*n) } // Measure memory access checks on a class property. // // Note: The end_unpaired_access forces a callback on the property's // materializeForSet! // --- // Hopefully the optimizer will not see this as "final" and optimize away the // materializeForSet. public class C { public var counter = 0 func inc() { counter += 1 } } // Thunk @inline(never) func updateClass(_ c: C) { c.inc() } // TODO: Replacing materializeForSet accessors with yield-once // accessors should make the callback overhead go away. @inline(never) public func run_accessInMatSet(_ n: Int) { let c = C() for _ in 1...10000*n { updateClass(c) } check(c.counter == 10000*n) } // Measure nested access to independent objects. // // A single access set is still faster than hashing for up to four accesses. // --- struct Var { var val = 0 } @inline(never) func update(a: inout Var, b: inout Var, c: inout Var, d: inout Var) { a.val += 1 b.val += 1 c.val += 1 d.val += 1 } @inline(never) public func run_accessIndependent(_ n: Int) { var a = Var() var b = Var() var c = Var() var d = Var() let updateVars = { update(a: &a, b: &b, c: &c, d: &d) } for _ in 1...1000*n { updateVars() } check(a.val == 1000*n && b.val == 1000*n && c.val == 1000*n && d.val == 1000*n) }