File: scaffolding.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (101 lines) | stat: -rw-r--r-- 3,374 bytes parent folder | download
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
//===----------------------------------------------------------------------===//
//
// 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()
    }
}