Skip to content

[StdLib][RFC][DNM] Add isIdentical Methods for Quick Comparisons to Array, ArraySlice, and ContiguousArray #82438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ set(SWIFT_BENCH_MODULES
single-source/ArrayOfRef
single-source/ArrayRemoveAll
single-source/ArraySetElement
single-source/ArraySliceTests
single-source/ArraySubscript
single-source/ArrayTests
single-source/AsyncTree
single-source/BinaryFloatingPointConversionFromBinaryInteger
single-source/BinaryFloatingPointProperties
Expand All @@ -65,6 +67,7 @@ set(SWIFT_BENCH_MODULES
single-source/ClassArrayGetter
single-source/CodableTest
single-source/Combos
single-source/ContiguousArrayTests
single-source/CountAlgo
single-source/DataBenchmarks
single-source/DeadArray
Expand Down
46 changes: 46 additions & 0 deletions benchmark/single-source/ArraySliceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===--- ArraySliceTests.swift --------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

import TestsUtils

public let benchmarks = [
BenchmarkInfo(name: "ArraySliceEqualUnique", runFunction: run_ArraySliceEqualUnique, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArraySliceEqualShared", runFunction: run_ArraySliceEqualShared, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArraySliceIdentical", runFunction: run_ArraySliceIdentical, tags: [.validation, .api, .Array]),
]

@inline(never)
public func run_ArraySliceEqualUnique(_ n: Int) {
let a1 = ArraySlice(0 ..< n)
let a2 = ArraySlice(0 ..< n)
for _ in 0 ..< 100_000 {
check(a1 == a2)
}
}

@inline(never)
public func run_ArraySliceEqualShared(_ n: Int) {
let a1 = ArraySlice(0 ..< n)
let a2 = a1
for _ in 0 ..< 100_000 {
check(a1 == a2)
}
}

@inline(never)
public func run_ArraySliceIdentical(_ n: Int) {
let a1 = ArraySlice(0 ..< n)
let a2 = a1
for _ in 0 ..< 100_000 {
check(a1.isIdentical(to: a2))
}
}
46 changes: 46 additions & 0 deletions benchmark/single-source/ArrayTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===--- ArrayTests.swift -------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

import TestsUtils

public let benchmarks = [
BenchmarkInfo(name: "ArrayEqualUnique", runFunction: run_ArrayEqualUnique, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayEqualShared", runFunction: run_ArrayEqualShared, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ArrayIdentical", runFunction: run_ArrayIdentical, tags: [.validation, .api, .Array]),
]

@inline(never)
public func run_ArrayEqualUnique(_ n: Int) {
let a1 = Array(0 ..< n)
let a2 = Array(0 ..< n)
for _ in 0 ..< 100_000 {
check(a1 == a2)
}
}

@inline(never)
public func run_ArrayEqualShared(_ n: Int) {
let a1 = Array(0 ..< n)
let a2 = a1
for _ in 0 ..< 100_000 {
check(a1 == a2)
}
}

@inline(never)
public func run_ArrayIdentical(_ n: Int) {
let a1 = Array(0 ..< n)
let a2 = a1
for _ in 0 ..< 100_000 {
check(a1.isIdentical(to: a2))
}
}
46 changes: 46 additions & 0 deletions benchmark/single-source/ContiguousArrayTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===--- ContiguousArrayTests.swift ---------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

import TestsUtils

public let benchmarks = [
BenchmarkInfo(name: "ContiguousArrayEqualUnique", runFunction: run_ContiguousArrayEqualUnique, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ContiguousArrayEqualShared", runFunction: run_ContiguousArrayEqualShared, tags: [.validation, .api, .Array]),
BenchmarkInfo(name: "ContiguousArrayIdentical", runFunction: run_ContiguousArrayIdentical, tags: [.validation, .api, .Array]),
]

@inline(never)
public func run_ContiguousArrayEqualUnique(_ n: Int) {
let a1 = ContiguousArray(0 ..< n)
let a2 = ContiguousArray(0 ..< n)
for _ in 0 ..< 100_000 {
check(a1 == a2)
}
}

@inline(never)
public func run_ContiguousArrayEqualShared(_ n: Int) {
let a1 = ContiguousArray(0 ..< n)
let a2 = a1
for _ in 0 ..< 100_000 {
check(a1 == a2)
}
}

@inline(never)
public func run_ContiguousArrayIdentical(_ n: Int) {
let a1 = ContiguousArray(0 ..< n)
let a2 = a1
for _ in 0 ..< 100_000 {
check(a1.isIdentical(to: a2))
}
}
7 changes: 7 additions & 0 deletions benchmark/utils/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ import ArrayOfPOD
import ArrayOfRef
import ArrayRemoveAll
import ArraySetElement
import ArraySliceTests
import ArraySubscript
import ArrayTests
import AsyncTree
import BinaryFloatingPointConversionFromBinaryInteger
import BinaryFloatingPointProperties
Expand All @@ -53,6 +55,7 @@ import Chars
import ClassArrayGetter
import CodableTest
import Combos
import ContiguousArrayTests
import CountAlgo
import CreateObjects
// rdar://128520766
Expand Down Expand Up @@ -228,7 +231,9 @@ register(ArrayOfPOD.benchmarks)
register(ArrayOfRef.benchmarks)
register(ArrayRemoveAll.benchmarks)
register(ArraySetElement.benchmarks)
register(ArraySliceTests.benchmarks)
register(ArraySubscript.benchmarks)
register(ArrayTests.benchmarks)
register(AsyncTree.benchmarks)
register(BinaryFloatingPointConversionFromBinaryInteger.benchmarks)
register(BinaryFloatingPointProperties.benchmarks)
Expand All @@ -253,8 +258,10 @@ register(CharacterRecognizer.benchmarks)
register(Chars.benchmarks)
register(CodableTest.benchmarks)
register(Combos.benchmarks)
register(ContiguousArrayTests.benchmarks)
register(CountAlgo.benchmarks)
register(ClassArrayGetter.benchmarks)
register(ContiguousArrayTests.benchmarks)
register(CreateObjects.benchmarks)
// rdar://128520766
// register(CxxSetToCollection.benchmarks)
Expand Down
30 changes: 30 additions & 0 deletions stdlib/public/core/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2157,3 +2157,33 @@ internal struct _ArrayAnyHashableBox<Element: Hashable>
}

extension Array: @unchecked Sendable where Element: Sendable { }

extension Array {
/// Returns a boolean value indicating whether this array is identical to
/// `other`.
///
/// Two array values are identical if there is no way to distinguish between
/// them.
///
/// Comparing arrays this way includes comparing (normally) hidden
/// implementation details such as the memory location of any underlying
/// array storage object. Therefore, identical arrays are guaranteed to
/// compare equal with `==`, but not all equal arrays are considered
/// identical.
///
/// - Performance: O(1)
@_alwaysEmitIntoClient
public func isIdentical(to other: Self) -> Bool {
let lhsCount = self.count
if lhsCount != other.count {
return false
}

// Test referential equality.
if unsafe lhsCount == 0 || self._buffer.identity == other._buffer.identity {
return true
}

return false
}
}
30 changes: 30 additions & 0 deletions stdlib/public/core/ArraySlice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1607,3 +1607,33 @@ extension ArraySlice {
}
}
#endif

extension ArraySlice {
/// Returns a boolean value indicating whether this array is identical to
/// `other`.
///
/// Two array values are identical if there is no way to distinguish between
/// them.
///
/// Comparing arrays this way includes comparing (normally) hidden
/// implementation details such as the memory location of any underlying
/// array storage object. Therefore, identical arrays are guaranteed to
/// compare equal with `==`, but not all equal arrays are considered
/// identical.
///
/// - Performance: O(1)
@_alwaysEmitIntoClient
public func isIdentical(to other: Self) -> Bool {
let lhsCount = self.count
if lhsCount != other.count {
return false
}

// Test referential equality.
if unsafe lhsCount == 0 || self._buffer.identity == other._buffer.identity {
return true
}

return false
}
}
30 changes: 30 additions & 0 deletions stdlib/public/core/ContiguousArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1515,3 +1515,33 @@ extension ContiguousArray {

extension ContiguousArray: @unchecked Sendable
where Element: Sendable { }

extension ContiguousArray {
/// Returns a boolean value indicating whether this array is identical to
/// `other`.
///
/// Two array values are identical if there is no way to distinguish between
/// them.
///
/// Comparing arrays this way includes comparing (normally) hidden
/// implementation details such as the memory location of any underlying
/// array storage object. Therefore, identical arrays are guaranteed to
/// compare equal with `==`, but not all equal arrays are considered
/// identical.
///
/// - Performance: O(1)
@_alwaysEmitIntoClient
public func isIdentical(to other: Self) -> Bool {
let lhsCount = self.count
if lhsCount != other.count {
return false
}

// Test referential equality.
if unsafe lhsCount == 0 || self._buffer.identity == other._buffer.identity {
return true
}

return false
}
}
43 changes: 43 additions & 0 deletions test/stdlib/ArraySliceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//
//
// RUN: %target-run-simple-swift(-parse-as-library)
// REQUIRES: executable_test
// END.
//
//===----------------------------------------------------------------------===//

import StdlibUnittest

@main
enum ArraySliceTests {
static func main() {
let testSuite = TestSuite("ArraySliceTests")
testSuite.test("Identical", testIdentical)
runAllTests()
}

static func testIdentical() {
let a1: ArraySlice = [0, 1, 2, 3]
expectTrue(a1.isIdentical(to: a1))

let a2: ArraySlice = a1
expectTrue(a1.isIdentical(to: a2))

var a3: ArraySlice = a2
a3.reserveCapacity(0)
expectFalse(a1.isIdentical(to: a3))

let a4: ArraySlice = [0, 1, 2, 3]
expectFalse(a1.isIdentical(to: a4))
}
}
43 changes: 43 additions & 0 deletions test/stdlib/ArrayTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//
//
// RUN: %target-run-simple-swift(-parse-as-library)
// REQUIRES: executable_test
// END.
//
//===----------------------------------------------------------------------===//

import StdlibUnittest

@main
enum ArrayTests {
static func main() {
let testSuite = TestSuite("ArrayTests")
testSuite.test("Identical", testIdentical)
runAllTests()
}

static func testIdentical() {
let a1: Array = [0, 1, 2, 3]
expectTrue(a1.isIdentical(to: a1))

let a2: Array = a1
expectTrue(a1.isIdentical(to: a2))

var a3: Array = a2
a3.reserveCapacity(0)
expectFalse(a1.isIdentical(to: a3))

let a4: Array = [0, 1, 2, 3]
expectFalse(a1.isIdentical(to: a4))
}
}
Loading