Skip to content

AttributedString Equatable conformance performance improvements #1287

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

Merged
merged 1 commit into from
May 13, 2025

Conversation

jmschonfeld
Copy link
Contributor

This commit includes a handful of performance improvements for AttributedString that provide some great speedups to its Equatable conformance and Runs view. These are the changes this makes:

AttributedString attribute raw value casting: for a little while we've known that comparing AttributedString attribute values is more expensive than we'd like. This fixes a few of those issues by:

  • Avoiding unnecessarily boxing the existential attribute value in an Optional which requires looking up type metadata and allocating space for the optional. This Optional in __equalAttributes was required previously but is no longer used
  • Avoid dynamic casts when casting the attribute values (as existential) to their expected types for fetching values and comparing values. We know that when comparing _AttributeValues that the raw value types must be the same, and when fetching a value from an AttributedString we know that the raw values must be of the expected key type. Instead of using as? casts (which go through dynamic casting machinery to support bridging types) we can simply use _identityCast to perform a much faster cast that just assumes the raw value is the type provided. I added debug-only asserts to help us quickly catch Foundation bugs where this isn't the case

I also noticed that most of the time spent in AttributedString's equatable conformance was in RangeSet operations. The way we were iterating the runs view was calling RangeSet APIs multiple times for the same information, and since RangeSet isn't fully frozen/inlinable, each call requires metadata allocation and extra costs. Instead, I refactored rangeIdx(containing:) to provide more info to the caller and provide a public assertion message so that the callers can avoid extra RangeSet calls that were already being made by this function.

Overall, these improvements made a 30% improvement in AttributedString's Equatable conformance and a 45% improvement in iterating the runs view via an attribute slice:

----------------------------------------------------------------------------------------------------------------------------
enumerateAttributesSlice-swift metrics
----------------------------------------------------------------------------------------------------------------------------

╒══════════════════════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│          Throughput (# / s) (K)          │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│                   main                   │        82 │        81 │        81 │        80 │        79 │        77 │        77 │        81 │
├──────────────────────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│                  branch                  │       119 │       117 │       117 │       116 │       115 │       114 │       113 │       117 │
├──────────────────────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│                    Δ                     │        37 │        36 │        36 │        36 │        36 │        37 │        36 │        36 │
├──────────────────────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│              Improvement %               │        45 │        44 │        44 │        45 │        46 │        48 │        47 │        36 │
╘══════════════════════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

----------------------------------------------------------------------------------------------------------------------------
equality metrics
----------------------------------------------------------------------------------------------------------------------------

╒══════════════════════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│          Throughput (# / s) (K)          │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│                   main                   │        79 │        79 │        78 │        77 │        76 │        76 │        76 │        78 │
├──────────────────────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│                  branch                  │       103 │       101 │       101 │        99 │        99 │        95 │        94 │       101 │
├──────────────────────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│                    Δ                     │        24 │        22 │        23 │        22 │        23 │        19 │        18 │        23 │
├──────────────────────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│              Improvement %               │        30 │        28 │        29 │        29 │        30 │        25 │        24 │        23 │
╘══════════════════════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

@jmschonfeld
Copy link
Contributor Author

@swift-ci please test

@jmschonfeld jmschonfeld merged commit 9f3a180 into swiftlang:main May 13, 2025
15 checks passed
@jmschonfeld jmschonfeld deleted the attrstr/equatable-perf branch May 13, 2025 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants