1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
//===--- 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)
}
|