Skip to content

Conversation

@itingliu
Copy link
Contributor

@itingliu itingliu commented Dec 4, 2025

We are currently eagerly calculating all components in _GregorianCalendar.dateComponents(:from:in:) even if they're not needed. Skip all those if they're not being requested at all.

Also switch from floating point's remainder(dividingBy:) and truncatingRemainder(dividingBy:) to integer math.

The use case in the original report is essentially the NextDatesMatchingOnHour benchmark, where only time components are requested. Here's the result for this one. For other existing benchmarks I'm seeing 1.5-2x of improvement.

// NextDatesMatchingOnHour

** BEFORE **

╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       0 │       0 │       0 │       0 │       0 │       0 │       0 │      28 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │       9 │       9 │       9 │       9 │       9 │       9 │       9 │      28 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     108 │     109 │     109 │     109 │     109 │     110 │     110 │      28 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

** AFTER **

╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       0 │       0 │       0 │       0 │       0 │       0 │       0 │      97 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │      33 │      33 │      32 │      32 │      32 │      31 │      31 │      97 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │      30 │      31 │      31 │      31 │      31 │      31 │      31 │      97 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

Fixes 165794940

We are currently eagerly calculating all components in `_GregorianCalendar.dateComponents(:from:in:)` even if they're not needed. Skip all those if they're not being requested at all.

Also switch from floating point's `remainder(dividingBy:)` and `truncatingRemainder(dividingBy:)` to integer math.

The use case in the original report is essentially the `NextDatesMatchingOnHour` benchmark, where only time components are requested. Here's the result for this one. For other existing benchmarks I'm seeing 1.5-2x of improvement.

```
// NextDatesMatchingOnHour

** BEFORE **

╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       0 │       0 │       0 │       0 │       0 │       0 │       0 │      28 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │       9 │       9 │       9 │       9 │       9 │       9 │       9 │      28 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │     108 │     109 │     109 │     109 │     109 │     110 │     110 │      28 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛

** AFTER **

╒═══════════════════════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╤═════════╕
│ Metric                    │      p0 │     p25 │     p50 │     p75 │     p90 │     p99 │    p100 │ Samples │
╞═══════════════════════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╪═════════╡
│ Malloc (total) *          │       0 │       0 │       0 │       0 │       0 │       0 │       0 │      97 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Throughput (# / s) (K)    │      33 │      33 │      32 │      32 │      32 │      31 │      31 │      97 │
├───────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
│ Time (total CPU) (μs) *   │      30 │      31 │      31 │      31 │      31 │      31 │      31 │      97 │
╘═══════════════════════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╧═════════╛
```

Fixes 165794940
@itingliu
Copy link
Contributor Author

itingliu commented Dec 4, 2025

@swift-ci please test

@itingliu itingliu merged commit 780ee8b into swiftlang:main Dec 6, 2025
21 checks passed
@itingliu itingliu deleted the pr/3594 branch December 6, 2025 00:12
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.

2 participants