File: PeakMemory.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 (77 lines) | stat: -rw-r--r-- 3,141 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
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 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 Swift project authors
*/

import Foundation
#if os(Windows)
import WinSDK
#endif

extension Benchmark {
    /// A peak memory footprint metric for the current process.
    public class PeakMemory: BenchmarkMetric {
        public static let identifier = "peak-memory"
        public static let displayName = "Peak memory footprint"
        
        private var memoryPeak: Int64?
        
        /// Creates a new instance and fetches the peak memory usage.
        public init() {
            memoryPeak = Self.peakMemory()
        }
        
        #if os(macOS) || os(iOS)
        private static func peakMemory() -> Int64? {
            // On macOS we use the Kernel framework to read a pretty accurate
            // memory footprint for the current task. The value reported here
            // is comparable to what Xcode displays in the debug memory gauge.
            var vmInfo = task_vm_info_data_t()
            var count = mach_msg_type_number_t(MemoryLayout<task_vm_info>.size) / 4
            let vmResult: kern_return_t = withUnsafeMutablePointer(to: &vmInfo) {
                $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                    task_info(mach_task_self_, task_flavor_t(TASK_VM_INFO), $0, &count)
                }
            }
            guard vmResult == KERN_SUCCESS else { return nil }
            return vmInfo.ledger_phys_footprint_peak
        }
        
        #elseif os(Linux) || os(Android)
        private static func peakMemory() -> Int64? {
            // On Linux we cannot use the Kernel framework, so we tap into the
            // kernel proc file system to read the vm peak reported in the process status.
            let statusFileURL = URL(fileURLWithPath: "/proc/self/status")

            guard let statusString = try? String(contentsOf: statusFileURL),
                let peakMemoryString = statusString.components(separatedBy: .newlines)
                    .first(where: { $0.hasPrefix("VmPeak") })?
                    .components(separatedBy: CharacterSet.decimalDigits.inverted)
                    .filter({ !$0.isEmpty })
                    .first,
                let peakMemory = Double(peakMemoryString) else { return nil }

            return Int64(peakMemory * 1024) // convert from KBytes to bytes
        }
        #elseif os(Windows)
        private static func peakMemory() -> Int64? {
            var pmcStats = PROCESS_MEMORY_COUNTERS()
            guard K32GetProcessMemoryInfo(
                GetCurrentProcess(),
                &pmcStats,
                DWORD(MemoryLayout<PROCESS_MEMORY_COUNTERS>.size)
            ) else { return nil }
            return Int64(pmcStats.PeakWorkingSetSize)
        }
        #endif
        
        public var result: MetricValue? {
            return memoryPeak.map(MetricValue.bytesInMemory)
        }
    }
}