//===--- RandomTree.swift -------------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 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 // //===----------------------------------------------------------------------===// // This test implements three competing versions of randomized binary trees, // indirectly testing reference counting performance. import TestsUtils var rng = SplitMix64(seed: 0) let count = 400 let input = (0 ..< count).shuffled(using: &rng) public let benchmarks = [ BenchmarkInfo( name: "RandomTree.insert.ADT", runFunction: run_ADT_insert, tags: [.validation, .algorithm, .refcount], setUpFunction: { blackHole(input) }), BenchmarkInfo( name: "RandomTree.insert.Unmanaged.slow", runFunction: run_SlowUnmanaged_insert, tags: [.validation, .algorithm, .refcount], setUpFunction: { blackHole(input) }), BenchmarkInfo( name: "RandomTree.insert.Unmanaged.fast", runFunction: run_FastUnmanaged_insert, tags: [.validation, .algorithm, .refcount], setUpFunction: { blackHole(input) }), BenchmarkInfo( name: "RandomTree.insert.UnsafePointer", runFunction: run_UnsafePointer_insert, tags: [.validation, .algorithm, .refcount], setUpFunction: { blackHole(input) }), ] enum EnumSearchTree { case empty indirect case node(EnumSearchTree, Element, EnumSearchTree) } extension EnumSearchTree { func forEach(_ body: (Element) -> Void) { switch self { case .empty: break case let .node(left, value, right): left.forEach(body) body(value) right.forEach(body) } } func contains(_ value: Element) -> Bool { switch self { case .empty: return false case let .node(left, v, right): if value == v { return true } return value < v ? left.contains(value) : right.contains(value) } } func inserting(_ value: __owned Element) -> EnumSearchTree { switch self { case .empty: return .node(.empty, value, .empty) case let .node(left, root, right): if value == root { return self } else if value < root { return .node(left.inserting(value), root, right) } else { return .node(left, root, right.inserting(value)) } } } } struct SlowUnmanagedSearchTree { class Node { var value: Element var left: SlowUnmanagedSearchTree var right: SlowUnmanagedSearchTree init( value: Element, left: SlowUnmanagedSearchTree = .empty, right: SlowUnmanagedSearchTree = .empty ) { self.left = left self.right = right self.value = value } } static var empty: SlowUnmanagedSearchTree { SlowUnmanagedSearchTree() } var root: Unmanaged? init() { self.root = nil } init(_root: Unmanaged?) { self.root = _root } } extension SlowUnmanagedSearchTree { mutating func deallocate() { guard let root = root?.takeRetainedValue() else { return } root.left.deallocate() root.right.deallocate() } } extension SlowUnmanagedSearchTree { func forEach(_ body: (Element) -> Void) { guard let root = root?.takeUnretainedValue() else { return } root.left.forEach(body) body(root.value) root.right.forEach(body) } func contains(_ value: Element) -> Bool { guard let root = root?.takeUnretainedValue() else { return false } if value == root.value { return true } return value < root.value ? root.left.contains(value) : root.right.contains(value) } mutating func insert(_ value: __owned Element) { guard let root = root?.takeUnretainedValue() else { self.root = Unmanaged.passRetained(Node(value: value)) return } if value == root.value { return } else if value < root.value { root.left.insert(value) } else { root.right.insert(value) } } } struct FastUnmanagedSearchTree { class Node { var value: Element var left: FastUnmanagedSearchTree var right: FastUnmanagedSearchTree init( value: Element, left: FastUnmanagedSearchTree = .empty, right: FastUnmanagedSearchTree = .empty ) { self.left = left self.right = right self.value = value } } static var empty: FastUnmanagedSearchTree { FastUnmanagedSearchTree() } var root: Unmanaged? init() { self.root = nil } init(_root: Unmanaged?) { self.root = _root } } extension FastUnmanagedSearchTree { mutating func deallocate() { guard let root = root else { return } root._withUnsafeGuaranteedRef { root in root.left.deallocate() root.right.deallocate() } root.release() } } extension FastUnmanagedSearchTree { func forEach(_ body: (Element) -> Void) { guard let root = root else { return } root._withUnsafeGuaranteedRef { root in root.left.forEach(body) body(root.value) root.right.forEach(body) } } func contains(_ value: Element) -> Bool { guard let root = root else { return false } return root._withUnsafeGuaranteedRef { root in if value == root.value { return true } return value < root.value ? root.left.contains(value) : root.right.contains(value) } } mutating func insert(_ value: __owned Element) { guard let root = root else { self.root = Unmanaged.passRetained(Node(value: value)) return } root._withUnsafeGuaranteedRef { root in if value == root.value { return } else if value < root.value { root.left.insert(value) } else { root.right.insert(value) } } } } struct PointerSearchTree { struct Node { var value: Element var left: PointerSearchTree = .empty var right: PointerSearchTree = .empty } static var empty: PointerSearchTree { PointerSearchTree() } var root: UnsafeMutablePointer? init() { self.root = nil } init(_root: UnsafeMutablePointer?) { self.root = _root } } extension PointerSearchTree { mutating func deallocate() { guard let root = root else { return } root.pointee.left.deallocate() root.pointee.right.deallocate() root.deallocate() } } extension PointerSearchTree { func forEach(_ body: (Element) -> Void) { guard let root = root else { return } root.pointee.left.forEach(body) body(root.pointee.value) root.pointee.right.forEach(body) } func contains(_ value: Element) -> Bool { guard let root = root else { return false } if value == root.pointee.value { return true } if value < root.pointee.value { return root.pointee.left.contains(value) } return root.pointee.right.contains(value) } mutating func insert(_ value: __owned Element) { guard let root = root else { let node = UnsafeMutablePointer.allocate(capacity: 1) node.initialize(to: Node(value: value)) self.root = node return } if value == root.pointee.value { return } else if value < root.pointee.value { root.pointee.left.insert(value) } else { root.pointee.right.insert(value) } } } func run_ADT_insert(_ iterations: Int) { for _ in 0 ..< iterations { var tree = identity(EnumSearchTree.empty) for value in input { tree = tree.inserting(value) } blackHole(tree) } } func run_SlowUnmanaged_insert(_ iterations: Int) { for _ in 0 ..< iterations { var tree = identity(SlowUnmanagedSearchTree.empty) for value in input { tree.insert(value) } blackHole(tree) tree.deallocate() } } func run_FastUnmanaged_insert(_ iterations: Int) { for _ in 0 ..< iterations { var tree = identity(FastUnmanagedSearchTree.empty) for value in input { tree.insert(value) } blackHole(tree) tree.deallocate() } } func run_UnsafePointer_insert(_ iterations: Int) { for _ in 0 ..< iterations { var tree = identity(PointerSearchTree.empty) for value in input { tree.insert(value) } blackHole(tree) tree.deallocate() } }