File: _HashTreeStatistics.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 (130 lines) | stat: -rw-r--r-- 4,841 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
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift Collections open source project
//
// Copyright (c) 2022 - 2024 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
//
//===----------------------------------------------------------------------===//

public struct _HashTreeStatistics {
  /// The number of nodes in the tree.
  public internal(set) var nodeCount: Int = 0

  /// The number of collision nodes in the tree.
  public internal(set) var collisionNodeCount: Int = 0

  /// The number of elements within this tree.
  public internal(set) var itemCount: Int = 0

  /// The number of elements whose keys have colliding hashes in the tree.
  public internal(set) var collisionCount: Int = 0

  /// The number of key comparisons that need to be done due to hash collisions
  /// when finding every key in the tree.
  public internal(set) var _collisionChainCount: Int = 0

  /// The maximum depth of the tree.
  public internal(set) var maxItemDepth: Int = 0

  internal var _sumItemDepth: Int = 0

  /// The sum of all storage within the tree that is available for item storage,
  /// measured in bytes. (This is storage is shared between actual
  /// items and child references. Depending on alignment issues, not all of
  /// this may be actually usable.)
  public internal(set) var capacityBytes: Int = 0

  /// The number of bytes of storage currently used for storing items.
  public internal(set) var itemBytes: Int = 0

  /// The number of bytes of storage currently used for storing child
  /// references.
  public internal(set) var childBytes: Int = 0

  /// The number of bytes currently available for storage, summed over all
  /// nodes in the tree.
  public internal(set) var freeBytes: Int = 0

  /// An estimate of the actual memory occupied by this tree. This includes
  /// not only storage space for items & children, but also the memory taken up
  /// by node headers and Swift's object headers.
  public internal(set) var grossBytes: Int = 0

  /// The average level of an item within this tree.
  public var averageItemDepth: Double {
    guard nodeCount > 0 else { return 0 }
    return Double(_sumItemDepth) / Double(itemCount)
  }
  /// An estimate of how efficiently this data structure manages memory.
  /// This is a value between 0 and 1 -- the ratio between how much space
  /// the actual stored data occupies and the overall number of bytes allocated
  /// for the entire data structure. (`itemBytes / grossBytes`)
  public var memoryEfficiency: Double {
    guard grossBytes > 0 else { return 1 }
    return Double(itemBytes) / Double(grossBytes)
  }

  public var averageNodeSize: Double {
    guard nodeCount > 0 else { return 0 }
    return Double(capacityBytes) / Double(nodeCount)
  }

  /// The average number of keys that need to be compared within the tree
  /// to find a member item. This is exactly 1 unless the tree contains hash
  /// collisions.
  public var averageLookupChainLength: Double {
    guard itemCount > 0 else { return 1 }
    return Double(itemCount + _collisionChainCount) / Double(itemCount)
  }

  internal init() {
    // Nothing to do
  }
}


extension _HashNode {
  internal func gatherStatistics(
    _ level: _HashLevel, _ stats: inout _HashTreeStatistics
  ) {
    // The empty singleton does not count as a node and occupies no space.
    if self.raw.storage === _emptySingleton { return }

    read {
      stats.nodeCount += 1
      stats.itemCount += $0.itemCount

      if isCollisionNode {
        stats.collisionNodeCount += 1
        stats.collisionCount += $0.itemCount
        stats._collisionChainCount += $0.itemCount * ($0.itemCount - 1) / 2
      }

      let keyStride = MemoryLayout<Key>.stride
      let valueStride = MemoryLayout<Value>.stride

      stats.maxItemDepth = Swift.max(stats.maxItemDepth, level.depth)
      stats._sumItemDepth += (level.depth + 1) * $0.itemCount
      stats.capacityBytes += $0.byteCapacity
      stats.freeBytes += $0.bytesFree
      stats.itemBytes += $0.itemCount * (keyStride + valueStride)
      stats.childBytes += $0.childCount * MemoryLayout<_RawHashNode>.stride

      let objectHeaderSize = 2 * MemoryLayout<Int>.stride

      // Note: for simplicity, we assume that there is no padding between
      // the object header and the storage header.
      let start = _getUnsafePointerToStoredProperties(self.raw.storage)
      let capacity = self.raw.storage.capacity
      let end = $0._memory + capacity * MemoryLayout<_RawHashNode>.stride
      stats.grossBytes += objectHeaderSize + (end - start)

      for child in $0.children {
        child.gatherStatistics(level.descend(), &stats)
      }
    }
  }
}