Skip to content
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

Yeet for_each #4060

Closed
wants to merge 2 commits into from
Closed

Yeet for_each #4060

wants to merge 2 commits into from

Conversation

james7132
Copy link
Member

@james7132 james7132 commented Feb 28, 2022

Objective

Fixes #4059. Remove for_each/for_each_mut from both Query and QueryState as it no longer is notably faster than the corresponding iter/iter_mut functions.

Solution

Yeet it!

Migration Guide

Query/QueryState::for_each(_mut) has been removed as it is no longer notably faster than iter/iter_mut. Use the iter implementation instead.

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Feb 28, 2022
@james7132 james7132 added A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change and removed S-Needs-Triage This issue needs to be labelled labels Feb 28, 2022
@mockersf
Copy link
Member

mockersf commented Feb 28, 2022

Was it confirmed by the benchmarks?

  • what was the numbers for world_query_for_each vs world_query_for_each?
  • what are the numbers for the benches busy_systems and contrived before and after the change?

@james7132
Copy link
Member Author

james7132 commented Feb 28, 2022

Reran the full suite between the two. Both the busy_systems and contrived benchmarks seem to be consistently a bit faster or at least not regressing, even when switched to iter_mut in those benchmarks! The world_query_* benchmarks before the change also are about the same too.

group                                         main                                   yeet-for-each
-----                                         ----                                   -------------
busy_systems/01x_entities_03_systems          1.05     73.7±1.34µs        ? ?/sec    1.00     70.1±0.86µs        ? ?/sec
busy_systems/01x_entities_06_systems          1.00    134.8±1.80µs        ? ?/sec    1.01    136.1±4.52µs        ? ?/sec
busy_systems/01x_entities_09_systems          1.00    206.1±2.62µs        ? ?/sec    1.00    206.0±6.10µs        ? ?/sec
busy_systems/01x_entities_12_systems          1.00    267.2±2.25µs        ? ?/sec    1.02   271.2±10.60µs        ? ?/sec
busy_systems/01x_entities_15_systems          1.04    338.4±5.33µs        ? ?/sec    1.00    324.5±2.85µs        ? ?/sec
busy_systems/02x_entities_03_systems          1.00    127.5±0.99µs        ? ?/sec    1.00    127.4±2.18µs        ? ?/sec
busy_systems/02x_entities_06_systems          1.01    257.2±2.29µs        ? ?/sec    1.00    255.7±3.79µs        ? ?/sec
busy_systems/02x_entities_09_systems          1.01    383.3±5.18µs        ? ?/sec    1.00    381.1±7.35µs        ? ?/sec
busy_systems/02x_entities_12_systems          1.00    503.5±4.35µs        ? ?/sec    1.00    501.8±5.93µs        ? ?/sec
busy_systems/02x_entities_15_systems          1.00    623.1±4.09µs        ? ?/sec    1.00    625.9±5.88µs        ? ?/sec
busy_systems/03x_entities_03_systems          1.04    193.0±2.07µs        ? ?/sec    1.00    186.1±7.75µs        ? ?/sec
busy_systems/03x_entities_06_systems          1.03    378.4±2.94µs        ? ?/sec    1.00    367.2±2.11µs        ? ?/sec
busy_systems/03x_entities_09_systems          1.03    566.5±5.09µs        ? ?/sec    1.00    547.5±4.80µs        ? ?/sec
busy_systems/03x_entities_12_systems          1.02    750.3±4.78µs        ? ?/sec    1.00    732.3±5.80µs        ? ?/sec
busy_systems/03x_entities_15_systems          1.04   944.1±11.94µs        ? ?/sec    1.00    910.2±6.50µs        ? ?/sec
busy_systems/04x_entities_03_systems          1.03    249.6±2.20µs        ? ?/sec    1.00    242.6±1.86µs        ? ?/sec
busy_systems/04x_entities_06_systems          1.01    497.7±3.82µs        ? ?/sec    1.00    490.8±6.37µs        ? ?/sec
busy_systems/04x_entities_09_systems          1.02    746.5±5.55µs        ? ?/sec    1.00   732.6±10.52µs        ? ?/sec
busy_systems/04x_entities_12_systems          1.00    989.5±6.96µs        ? ?/sec    1.01  1001.6±29.62µs        ? ?/sec
busy_systems/04x_entities_15_systems          1.02  1247.6±14.22µs        ? ?/sec    1.00  1220.4±11.66µs        ? ?/sec
busy_systems/05x_entities_03_systems          1.11    334.4±6.80µs        ? ?/sec    1.00    301.8±3.12µs        ? ?/sec
busy_systems/05x_entities_06_systems          1.09    659.8±6.49µs        ? ?/sec    1.00   605.5±10.13µs        ? ?/sec
busy_systems/05x_entities_09_systems          1.11    990.8±8.37µs        ? ?/sec    1.00   894.5±10.58µs        ? ?/sec
busy_systems/05x_entities_12_systems          1.11   1317.8±9.40µs        ? ?/sec    1.00  1191.2±11.66µs        ? ?/sec
busy_systems/05x_entities_15_systems          1.11  1652.8±17.75µs        ? ?/sec    1.00   1487.2±9.73µs        ? ?/sec
contrived/01x_entities_03_systems             1.05     41.9±0.57µs        ? ?/sec    1.00     39.8±2.00µs        ? ?/sec
contrived/01x_entities_06_systems             1.12     77.6±1.87µs        ? ?/sec    1.00     69.1±2.75µs        ? ?/sec
contrived/01x_entities_09_systems             1.06    111.8±1.69µs        ? ?/sec    1.00    105.1±4.21µs        ? ?/sec
contrived/01x_entities_12_systems             1.03    146.2±1.08µs        ? ?/sec    1.00    141.5±4.79µs        ? ?/sec
contrived/01x_entities_15_systems             1.06    181.4±2.07µs        ? ?/sec    1.00    171.9±5.89µs        ? ?/sec
contrived/02x_entities_03_systems             1.02     60.2±0.53µs        ? ?/sec    1.00     59.1±1.44µs        ? ?/sec
contrived/02x_entities_06_systems             1.02    123.9±1.16µs        ? ?/sec    1.00    120.9±4.71µs        ? ?/sec
contrived/02x_entities_09_systems             1.02    180.8±1.58µs        ? ?/sec    1.00    177.5±4.12µs        ? ?/sec
contrived/02x_entities_12_systems             1.06    240.9±2.10µs        ? ?/sec    1.00    227.5±3.66µs        ? ?/sec
contrived/02x_entities_15_systems             1.04    298.5±2.42µs        ? ?/sec    1.00    287.7±5.19µs        ? ?/sec
contrived/03x_entities_03_systems             1.04     88.8±0.78µs        ? ?/sec    1.00     85.4±2.14µs        ? ?/sec
contrived/03x_entities_06_systems             1.04    172.3±1.59µs        ? ?/sec    1.00    165.1±3.54µs        ? ?/sec
contrived/03x_entities_09_systems             1.05    256.7±1.94µs        ? ?/sec    1.00    244.0±5.59µs        ? ?/sec
contrived/03x_entities_12_systems             1.03    334.8±2.10µs        ? ?/sec    1.00    325.1±5.68µs        ? ?/sec
contrived/03x_entities_15_systems             1.04    422.1±2.56µs        ? ?/sec    1.00    406.5±6.09µs        ? ?/sec
contrived/04x_entities_03_systems             1.01    108.1±1.04µs        ? ?/sec    1.00    106.7±3.16µs        ? ?/sec
contrived/04x_entities_06_systems             1.04    220.4±1.99µs        ? ?/sec    1.00    212.9±7.28µs        ? ?/sec
contrived/04x_entities_09_systems             1.03    322.8±2.00µs        ? ?/sec    1.00    314.5±4.71µs        ? ?/sec
contrived/04x_entities_12_systems             1.00    433.7±2.75µs        ? ?/sec    1.01   437.6±17.71µs        ? ?/sec
contrived/04x_entities_15_systems             1.00    534.0±5.91µs        ? ?/sec    1.03   549.5±19.60µs        ? ?/sec
contrived/05x_entities_03_systems             1.00    133.2±1.09µs        ? ?/sec    1.00    133.1±2.35µs        ? ?/sec
contrived/05x_entities_06_systems             1.04    267.4±1.71µs        ? ?/sec    1.00    258.1±3.28µs        ? ?/sec
contrived/05x_entities_09_systems             1.02    397.2±3.08µs        ? ?/sec    1.00    387.5±3.61µs        ? ?/sec
contrived/05x_entities_12_systems             1.00    524.9±4.77µs        ? ?/sec    1.00   524.6±16.65µs        ? ?/sec
contrived/05x_entities_15_systems             1.03   663.4±10.91µs        ? ?/sec    1.00    643.8±7.88µs        ? ?/sec
empty_commands/0_entities                     1.04      5.4±0.33ns        ? ?/sec    1.00      5.2±0.05ns        ? ?/sec
empty_systems/000_systems                     2.11      2.8±0.27µs        ? ?/sec    1.00  1306.3±84.60ns        ? ?/sec
empty_systems/001_systems                     1.00      5.2±0.36µs        ? ?/sec    1.20      6.2±0.29µs        ? ?/sec
empty_systems/002_systems                     1.00      6.4±0.30µs        ? ?/sec    1.07      6.8±0.26µs        ? ?/sec
empty_systems/003_systems                     1.04      8.1±0.45µs        ? ?/sec    1.00      7.8±0.53µs        ? ?/sec
empty_systems/004_systems                     1.00      8.8±0.35µs        ? ?/sec    1.01      8.9±0.30µs        ? ?/sec
empty_systems/005_systems                     1.07     10.5±0.78µs        ? ?/sec    1.00      9.8±0.61µs        ? ?/sec
empty_systems/010_systems                     1.03     15.8±0.81µs        ? ?/sec    1.00     15.4±1.02µs        ? ?/sec
empty_systems/015_systems                     1.06     22.4±0.75µs        ? ?/sec    1.00     21.3±2.02µs        ? ?/sec
empty_systems/020_systems                     1.05     27.4±1.08µs        ? ?/sec    1.00     26.1±2.76µs        ? ?/sec
empty_systems/025_systems                     1.19     33.1±1.45µs        ? ?/sec    1.00     27.9±2.86µs        ? ?/sec
empty_systems/030_systems                     1.11     39.0±1.32µs        ? ?/sec    1.00     35.0±3.40µs        ? ?/sec
empty_systems/035_systems                     1.20     45.2±2.35µs        ? ?/sec    1.00     37.7±2.26µs        ? ?/sec
empty_systems/040_systems                     1.19     50.8±1.76µs        ? ?/sec    1.00     42.8±3.76µs        ? ?/sec
empty_systems/045_systems                     1.17     56.4±1.78µs        ? ?/sec    1.00     48.2±3.23µs        ? ?/sec
empty_systems/050_systems                     1.29     63.1±2.10µs        ? ?/sec    1.00     49.1±2.43µs        ? ?/sec
empty_systems/055_systems                     1.23     67.2±2.28µs        ? ?/sec    1.00     54.8±4.00µs        ? ?/sec
empty_systems/060_systems                     1.28     73.1±2.23µs        ? ?/sec    1.00     57.3±3.88µs        ? ?/sec
empty_systems/065_systems                     1.25     79.0±2.54µs        ? ?/sec    1.00     63.2±4.98µs        ? ?/sec
empty_systems/070_systems                     1.30     86.2±2.01µs        ? ?/sec    1.00     66.5±5.42µs        ? ?/sec
empty_systems/075_systems                     1.25     90.3±2.75µs        ? ?/sec    1.00     72.2±4.85µs        ? ?/sec
empty_systems/080_systems                     1.30     96.0±2.88µs        ? ?/sec    1.00     73.8±4.07µs        ? ?/sec
empty_systems/085_systems                     1.32    101.2±2.75µs        ? ?/sec    1.00     76.8±3.29µs        ? ?/sec
empty_systems/090_systems                     1.31    108.3±3.61µs        ? ?/sec    1.00     82.4±5.56µs        ? ?/sec
empty_systems/095_systems                     1.33   115.8±12.53µs        ? ?/sec    1.00     87.4±5.02µs        ? ?/sec
empty_systems/100_systems                     1.29    117.7±5.25µs        ? ?/sec    1.00     91.1±4.04µs        ? ?/sec
fake_commands/2000_commands                   1.00      7.2±0.05µs        ? ?/sec    1.06      7.7±0.16µs        ? ?/sec
fake_commands/4000_commands                   1.00     16.3±0.55µs        ? ?/sec    1.01     16.4±0.46µs        ? ?/sec
fake_commands/6000_commands                   1.10     23.6±0.99µs        ? ?/sec    1.00     21.5±0.08µs        ? ?/sec
fake_commands/8000_commands                   1.00     28.8±0.19µs        ? ?/sec    1.15     33.2±0.67µs        ? ?/sec
for_each_iter                                 1.00     29.6±2.52ms        ? ?/sec    1.06     31.4±0.36ms        ? ?/sec
for_each_par_iter/threads/1                   1.00     17.4±1.05ms        ? ?/sec    1.09     19.1±0.87ms        ? ?/sec
for_each_par_iter/threads/16                  1.08      2.5±0.06ms        ? ?/sec    1.00      2.3±0.08ms        ? ?/sec
for_each_par_iter/threads/2                   1.00     10.5±0.26ms        ? ?/sec    1.01     10.5±0.35ms        ? ?/sec
for_each_par_iter/threads/32                  1.07      2.3±0.04ms        ? ?/sec    1.00      2.1±0.04ms        ? ?/sec
for_each_par_iter/threads/4                   1.00      6.4±0.12ms        ? ?/sec    1.04      6.6±0.19ms        ? ?/sec
for_each_par_iter/threads/8                   1.03      3.7±0.09ms        ? ?/sec    1.00      3.6±0.05ms        ? ?/sec
get_or_spawn/batched                          1.19   494.7±53.04µs        ? ?/sec    1.00   417.1±47.45µs        ? ?/sec
get_or_spawn/individual                       1.11  999.9±128.88µs        ? ?/sec    1.00   900.6±91.97µs        ? ?/sec
insert_commands/insert                        1.01   787.9±47.75µs        ? ?/sec    1.00   779.6±31.87µs        ? ?/sec
insert_commands/insert_batch                  1.14   539.4±80.43µs        ? ?/sec    1.00   473.0±46.73µs        ? ?/sec
many_maps_iter                                1.00     27.2±0.05ms        ? ?/sec    1.00     27.2±0.26ms        ? ?/sec
many_maps_par_iter/threads/1                  1.00     17.4±0.87ms        ? ?/sec    1.01     17.5±0.72ms        ? ?/sec
many_maps_par_iter/threads/16                 1.05      2.3±0.04ms        ? ?/sec    1.00      2.2±0.06ms        ? ?/sec
many_maps_par_iter/threads/2                  1.00     10.4±0.32ms        ? ?/sec    1.02     10.6±0.30ms        ? ?/sec
many_maps_par_iter/threads/32                 1.01      2.1±0.02ms        ? ?/sec    1.00      2.1±0.04ms        ? ?/sec
many_maps_par_iter/threads/4                  1.00      6.4±0.10ms        ? ?/sec    1.01      6.5±0.14ms        ? ?/sec
many_maps_par_iter/threads/8                  1.01      3.6±0.04ms        ? ?/sec    1.00      3.6±0.03ms        ? ?/sec
overhead_par_iter/threads/1                   1.03     48.3±2.94µs        ? ?/sec    1.00     46.8±1.45µs        ? ?/sec
overhead_par_iter/threads/16                  1.00     88.4±3.56µs        ? ?/sec    1.03     90.7±3.15µs        ? ?/sec
overhead_par_iter/threads/2                   1.00     54.1±1.95µs        ? ?/sec    1.05     56.9±2.35µs        ? ?/sec
overhead_par_iter/threads/32                  1.09     92.0±3.37µs        ? ?/sec    1.00     84.2±2.72µs        ? ?/sec
overhead_par_iter/threads/4                   1.00     69.7±5.01µs        ? ?/sec    1.01     70.6±2.34µs        ? ?/sec
overhead_par_iter/threads/8                   1.00     82.7±2.89µs        ? ?/sec    1.03     85.1±4.38µs        ? ?/sec
sized_commands_0_bytes/2000_commands          1.00      5.4±0.40µs        ? ?/sec    1.01      5.4±0.03µs        ? ?/sec
sized_commands_0_bytes/4000_commands          1.00     11.5±0.15µs        ? ?/sec    1.02     11.8±0.30µs        ? ?/sec
sized_commands_0_bytes/6000_commands          1.08     16.5±0.10µs        ? ?/sec    1.00     15.3±0.06µs        ? ?/sec
sized_commands_0_bytes/8000_commands          1.00     20.3±0.21µs        ? ?/sec    1.16     23.6±0.66µs        ? ?/sec
sized_commands_12_bytes/2000_commands         1.00      7.4±0.06µs        ? ?/sec    1.09      8.1±0.10µs        ? ?/sec
sized_commands_12_bytes/4000_commands         1.00     15.6±0.50µs        ? ?/sec    1.06     16.6±1.14µs        ? ?/sec
sized_commands_12_bytes/6000_commands         1.00     22.7±0.16µs        ? ?/sec    1.10     24.9±0.94µs        ? ?/sec
sized_commands_12_bytes/8000_commands         1.00     30.1±0.30µs        ? ?/sec    1.00     30.1±0.17µs        ? ?/sec
sized_commands_512_bytes/2000_commands        1.11    163.5±5.54µs        ? ?/sec    1.00    147.5±5.65µs        ? ?/sec
sized_commands_512_bytes/4000_commands        1.31   373.2±30.76µs        ? ?/sec    1.00   283.8±24.48µs        ? ?/sec
sized_commands_512_bytes/6000_commands        1.10   510.0±59.47µs        ? ?/sec    1.00   464.7±70.70µs        ? ?/sec
sized_commands_512_bytes/8000_commands        1.00   671.4±67.91µs        ? ?/sec    1.00   669.3±75.12µs        ? ?/sec
spawn_commands/2000_entities                  1.00    245.4±6.62µs        ? ?/sec    1.09    266.8±9.55µs        ? ?/sec
spawn_commands/4000_entities                  1.01   549.7±36.02µs        ? ?/sec    1.00   546.5±25.92µs        ? ?/sec
spawn_commands/6000_entities                  1.07   791.0±53.90µs        ? ?/sec    1.00   735.9±56.35µs        ? ?/sec
spawn_commands/8000_entities                  1.00   943.5±35.90µs        ? ?/sec    1.00   945.8±51.39µs        ? ?/sec
world_entity/50000_entities                   1.00    423.4±0.61µs        ? ?/sec    1.06   448.8±31.16µs        ? ?/sec
world_get/50000_entities_sparse               1.11   623.6±24.55µs        ? ?/sec    1.00   561.5±10.30µs        ? ?/sec
world_get/50000_entities_table                1.00   966.1±42.47µs        ? ?/sec    1.03   991.1±51.66µs        ? ?/sec
world_query_for_each/50000_entities_sparse    1.00    109.5±1.02µs        ? ?/sec
world_query_for_each/50000_entities_table     1.00     27.1±0.09µs        ? ?/sec
world_query_get/50000_entities_sparse         1.00    548.0±6.99µs        ? ?/sec    1.05   574.1±21.06µs        ? ?/sec
world_query_get/50000_entities_table          1.00    328.9±6.20µs        ? ?/sec    1.15    377.5±8.10µs        ? ?/sec
world_query_iter/50000_entities_sparse        1.02    118.6±6.00µs        ? ?/sec    1.00    116.5±0.93µs        ? ?/sec
world_query_iter/50000_entities_table         1.13     30.9±0.89µs        ? ?/sec    1.00     27.3±1.14µs        ? ?/sec

@alice-i-cecile alice-i-cecile added the C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide label Feb 28, 2022
@bjorn3 bjorn3 added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Feb 28, 2022
@cart
Copy link
Member

cart commented Feb 28, 2022

The benchmarks in the Bevy repo don't cover some of the cases that motivated for_each:

I re-ran the benchmarks in my ecs_bench_suite fork (I ported to bevy main, but havent pushed those changes yet):
image

Note the stark differences in frag_iter performance.

However once we add black_box to the benches (which ecs_bench_suite doesn't currently do as a matter of policy) the gap does close:

image

However its hard to say how often we'd get the "non-black-boxed" perf in the real world. And even if black_box is the "real world perf", there is still a measurable difference for fragmented iterators.

@cart
Copy link
Member

cart commented Feb 28, 2022

Some clarifying points:

  • The red "diffs" in the second image are relative to the "non black boxed" benchmarks. They illustrate the "cost" of adding black_box to the systems
  • I black boxed every iterated system parameter (and nothing else).

@cart
Copy link
Member

cart commented Feb 28, 2022

Whether or not we should treat black_box benches as our source of truth is an open debate in my mind. My understanding of black_box is that it is supposed to force the benchmark to behave like a "real world" program by preventing the compiler from "optimizing out" things that it wouldn't in the real world
But what happens if we construct a "real world" benchmark? What if it still performs better without black_box?
Or put another way: black_box might be preventing a class of optimization that is valuable in real world programs.
And if that is true, removing for_each might remove our ability to optimize in that way, given the stark difference in fragemented iter performance between foreach and iter in non-black-boxed systems.

@alice-i-cecile alice-i-cecile added the C-Performance A change motivated by improving speed, memory usage or compile times label Mar 1, 2022
@james7132
Copy link
Member Author

I black boxed every iterated system parameter (and nothing else).

This was brought up in Discord, but the compiler will also optimize out any expression where the output is not being used too. https://gendignoux.com/blog/2022/01/31/rust-benchmarks.html. Not sure if the ecs_bench suite change you made also includes this. If the mutable reference was black_box'ed then dereferenced, the compiler has to assume that there's some alteration to the referenced value, instead of a typical MulAssign call it would otherwise make, which might add enough to mask over any performance gains for_each makes. Note: unless the compiler can prove that the swap in bevy's benchmarks is effectively a no-op, they shouldn't have this issue, but you still see the difference disappear there too. This would also establish a pretty low bar for breaking that optimization.

But what happens if we construct a "real world" benchmark? What if it still performs better without black_box?

I agree, black_box is meant to produce the worst case results given a particular implementation as a means of establishing an upper bound. However, I think this is more where a proof by contradiction is strictly required. Most of these benchmarks have toy or trivially optimizable cases. We'd need a non-trivial real world case that demonstrates this and where the internal computation doesn't dwarf the cost of iteration.

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label Apr 25, 2022
@alice-i-cecile alice-i-cecile added the S-Needs-Benchmarking This set of changes needs performance benchmarking to double-check that they help label May 18, 2022
@mockersf mockersf mentioned this pull request Jun 21, 2022
2 tasks
@Weibye Weibye added S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! and removed S-Adopt-Me The original PR author has no intent to complete this work. Pick me up! labels Aug 10, 2022
@CGMossa
Copy link
Contributor

CGMossa commented Nov 4, 2022

Is this still a goal?

@james7132
Copy link
Member Author

@CGMossa this is indeed still a goal and contingent on improving the base iterator's perf across the board until it matches for_each.

That said quite a few PRs have been merged since this was first opened. As of #6461, on my machine, the two are identical in all but fragmented iteration. Of which, wider queries are identical in perf, and the smaller queries differ by less than 100ns. It might be due time to revisit this and consider if such a perf benefit is worth the maintenance cost of keeping these separate and the mental burden of choosing one over the other for end users.

@cart
Copy link
Member

cart commented Nov 15, 2022

Closing this as code has diverged a lot and a new set of benches would be required to justify the change. Definitely open to the idea if the numbers are compelling!

@cart cart closed this Nov 15, 2022
@james7132
Copy link
Member Author

Going to leave it as is, since #6547 introduces a much wider performance gap between for_each and iter due to the former more easily autovectorizing. Will come back to this when we can make iter do the same.

@james7132 james7132 deleted the yeet-for-each branch November 18, 2022 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide C-Code-Quality A section of code that is hard to understand or change C-Performance A change motivated by improving speed, memory usage or compile times S-Needs-Benchmarking This set of changes needs performance benchmarking to double-check that they help S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove for_each and for_each_mut APIs
7 participants