swift-nio/IntegrationTests/allocation-counter-tests-fr.../template/scaffolding.swift

165 lines
5.3 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import Foundation
import AtomicCounter
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
import Darwin
#else
import Glibc
#endif
func waitForThreadsToQuiesce(shouldReachZero: Bool) {
func getUnfreed() -> Int {
return AtomicCounter.read_malloc_counter() - AtomicCounter.read_free_counter()
}
var oldNumberOfUnfreed = getUnfreed()
var count = 0
repeat {
guard count < 100 else {
print("WARNING: Giving up, shouldReachZero=\(shouldReachZero), unfreeds=\(oldNumberOfUnfreed)")
return
}
count += 1
usleep(shouldReachZero ? 50_000 : 200_000) // allocs/frees happen on multiple threads, allow some cool down time
let newNumberOfUnfreed = getUnfreed()
if oldNumberOfUnfreed == newNumberOfUnfreed && (!shouldReachZero || newNumberOfUnfreed <= 0) {
// nothing happened in the last 100ms, let's assume everything's
// calmed down already.
if count > 5 || newNumberOfUnfreed != 0 {
print("DEBUG: After waiting \(count) times, we quiesced to unfreeds=\(newNumberOfUnfreed)")
}
return
}
oldNumberOfUnfreed = newNumberOfUnfreed
} while true
}
func measureAll(_ fn: () -> Int) -> [[String: Int]] {
func measureOne(throwAway: Bool = false, _ fn: () -> Int) -> [String: Int]? {
AtomicCounter.reset_free_counter()
AtomicCounter.reset_malloc_counter()
AtomicCounter.reset_malloc_bytes_counter()
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
autoreleasepool {
_ = fn()
}
#else
_ = fn()
#endif
waitForThreadsToQuiesce(shouldReachZero: !throwAway)
let frees = AtomicCounter.read_free_counter()
let mallocs = AtomicCounter.read_malloc_counter()
let mallocedBytes = AtomicCounter.read_malloc_bytes_counter()
if mallocs - frees < 0 {
print("WARNING: negative remaining allocation count, skipping.")
return nil
}
return [
"total_allocations": mallocs,
"total_allocated_bytes": mallocedBytes,
"remaining_allocations": mallocs - frees
]
}
_ = measureOne(throwAway: true, fn) /* pre-heat and throw away */
var measurements: [[String: Int]] = []
for _ in 0..<10 {
if let results = measureOne(fn) {
measurements.append(results)
}
}
return measurements
}
func measureAndPrint(desc: String, fn: () -> Int) -> Void {
let measurements = measureAll(fn)
for k in measurements[0].keys {
let vs = measurements.map { $0[k]! }
print("\(desc).\(k): \(vs.min() ?? -1)")
}
print("DEBUG: \(measurements)")
}
public func measure(identifier: String, _ body: () -> Int) {
measureAndPrint(desc: identifier) {
return body()
}
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
func measureAll(_ fn: @escaping () async -> Int) -> [[String: Int]] {
func measureOne(throwAway: Bool = false, _ fn: @escaping () async -> Int) -> [String: Int]? {
func run(_ fn: @escaping () async -> Int) {
let group = DispatchGroup()
group.enter()
Task {
_ = await fn()
group.leave()
}
group.wait()
}
AtomicCounter.reset_free_counter()
AtomicCounter.reset_malloc_counter()
AtomicCounter.reset_malloc_bytes_counter()
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
autoreleasepool {
run(fn)
}
#else
run(fn)
#endif
waitForThreadsToQuiesce(shouldReachZero: !throwAway)
let frees = AtomicCounter.read_free_counter()
let mallocs = AtomicCounter.read_malloc_counter()
let mallocedBytes = AtomicCounter.read_malloc_bytes_counter()
if mallocs - frees < 0 {
print("WARNING: negative remaining allocation count, skipping.")
return nil
}
return [
"total_allocations": mallocs,
"total_allocated_bytes": mallocedBytes,
"remaining_allocations": mallocs - frees
]
}
_ = measureOne(throwAway: true, fn) /* pre-heat and throw away */
var measurements: [[String: Int]] = []
for _ in 0..<10 {
if let results = measureOne(fn) {
measurements.append(results)
}
}
return measurements
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
func measureAndPrint(desc: String, fn: @escaping () async -> Int) -> Void {
let measurements = measureAll(fn)
for k in measurements[0].keys {
let vs = measurements.map { $0[k]! }
print("\(desc).\(k): \(vs.min() ?? -1)")
}
print("DEBUG: \(measurements)")
}
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
public func measure(identifier: String, _ body: @escaping () async -> Int) {
measureAndPrint(desc: identifier, fn: body)
}