Skip to content

Conversation

@gorsing
Copy link
Contributor

@gorsing gorsing commented Dec 21, 2025

Description:
Adds high-performance, inlined any and all predicates to Array in dmd.root.array. These templates use alias predicates to ensure zero-overhead execution, facilitating the refactoring of manual for loops in semantic analysis stages.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @gorsing! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#22284"

@gorsing gorsing marked this pull request as draft December 21, 2025 13:11
@gorsing gorsing force-pushed the root-array-predicates branch from 57f90a4 to 98f258c Compare December 21, 2025 14:43
@gorsing gorsing marked this pull request as ready for review December 21, 2025 14:51
@dkorpel
Copy link
Contributor

dkorpel commented Dec 21, 2025

Can you include some examples of where you want to use these functions?

@gorsing gorsing force-pushed the root-array-predicates branch from 98f258c to 63d8497 Compare December 21, 2025 20:46
@gorsing
Copy link
Contributor Author

gorsing commented Dec 21, 2025

Hi @dkorpel, thanks for the question.
I've updated dmd/semantic3.d to use these new functions. You can see how they replace manual for and foreach loops with more concise any and all predicates.

Copy link
Contributor

@dkorpel dkorpel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the examples. Unfortunately I don't think they demonstrate benefits. It would be helpful if it collapsed complex loop logic into something simpler, but replacing a foreach loop body with a function literal passed to a call to all or any only makes the code harder to read in my opinion, as it's now harder to recognize that a loop is being performed.

//fens = null;
}
}
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example adds a return statement, which is discarded.

Comment on lines 1283 to 1286
const allDefined = funcdecl.labtab.tab.asRange.all!(kv =>
(cast(LabelDsymbol)kv.value).statement ||
((cast(LabelDsymbol)kv.value).deleted && !(cast(LabelDsymbol)kv.value).iasm)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds more code, doing an additional loop which is redundant because the original loop is unchanged.

return true;
}
}
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an improvement relative to for, but relative to the more obvious refactor to foreach it only adds another discarded return value.

if (f2 == f)
break LcheckAncestorsOfANestedRef;
}
if (a[].any!(f2 => f2 == f))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good loop replacement, but can already be done better with a.contains(f).

@gorsing
Copy link
Contributor Author

gorsing commented Dec 21, 2025

Thanks for the feedback, @dkorpel.
​You're right, these specific spots in semantic3.d weren't the best choice for functional primitives, especially since they either introduced redundant loops or made the logic less readable compared to a standard foreach. I will revert the changes in this file.
​I also take your point about using a.contains(f) instead of a manual any call for simple membership checks.
​Out of curiosity, are there specific areas in the DMD codebase where you feel all or any are preferred? Is it mostly for collapsing deeply nested/complex loop logic, or is there a general preference for foreach even in those cases to keep the 'loop' nature of the code explicit?"

@dkorpel
Copy link
Contributor

dkorpel commented Dec 21, 2025

​Out of curiosity, are there specific areas in the DMD codebase where you feel all or any are preferred?

Not really. I can think of many places where a loop is used to find an element in a list. any can be used for that, but find or contains are a better fit.

Is it mostly for collapsing deeply nested/complex loop logic, or is there a general preference for foreach even in those cases to keep the 'loop' nature of the code explicit?"

If you can formulate a piece of loop code as a mathematical question (like the aforementioned "does this list contain this element?") then the loop could be seen as an implementation detail, justifying the use of range functions. In dmd loops often have side effects and do more than produce a boolean result (computing types, printing errors, rewriting elements), in which case trying to hide the foreach part behind a functional interface is only obfuscating in my opinion.

@gorsing gorsing marked this pull request as draft December 22, 2025 16:42
@gorsing gorsing force-pushed the root-array-predicates branch from 63d8497 to be73bef Compare December 24, 2025 16:17
@gorsing
Copy link
Contributor Author

gorsing commented Dec 24, 2025

Hi @dkorpel Thanks for the previous review

I have updated our base Array!T container with several high-performance methods O(N). These are the modern practices found in Rust, LLVM, and modern C++ that were missing from our implementation.
Why this is important:

Instant Deletion (swapRemove):

  • Before: O(N) (Slow: shifting all elements after the index).
  • After: O(1) (Instant: swaps the target with the last element).
  • Industry Standard: Matches vec.swap_remove() in Rust.

Efficient Filtering (removeIf):

  • Before: O(N2) (Very slow: repeated deletions cause nested shifts).
  • After: O(N) (Fast: the "Erase-Remove" idiom, processes the whole array in a single pass).
  • Industry Standard: Matches vec.retain() in Rust and std::erase_if in C++20.

Memory Management (shrinkToFit):

  • The array can now release unused capacity back to the system or move data back to the fast SSO (Small Size Optimization) buffer.
  • This is critical for preventing memory bloating in long-running processes.

Cleaner Code (any, all):

  • Replaces bulky foreach loops with expressive, functional-style checks.
  • Uses short-circuiting: the search stops the moment the result is known, saving CPU cycles.

@gorsing gorsing marked this pull request as ready for review December 24, 2025 16:46
@gorsing gorsing requested a review from dkorpel December 24, 2025 16:46
@gorsing
Copy link
Contributor Author

gorsing commented Dec 24, 2025

I have taken out some parts of MR as separate problems
#22300
#22301

@dkorpel
Copy link
Contributor

dkorpel commented Dec 24, 2025

I have taken out some parts of MR as separate problems

Thanks for doing that!

@dkorpel
Copy link
Contributor

dkorpel commented Dec 24, 2025

I have updated our base Array!T container with several high-performance methods O(N).

Do you have benchmarks showing how much this improves compilation times? Note that the dmd.root.array module is not a complete general purpose array implementation, it is only used for dmd to make bootstrapping easier. Hence additions to that module need to be motivated through dmd's use cases.

@gorsing gorsing marked this pull request as draft December 25, 2025 10:19
@gorsing gorsing closed this Dec 26, 2025
@gorsing gorsing force-pushed the root-array-predicates branch from 4ce58ec to f4853eb Compare December 26, 2025 10:42
@gorsing gorsing reopened this Dec 26, 2025
@gorsing
Copy link
Contributor Author

gorsing commented Dec 30, 2025

Hi @dkorpel
I've highlighted the changes to the old code in #22327 to separate the addition of new methods from fixes or improvements.

@gorsing
Copy link
Contributor Author

gorsing commented Dec 30, 2025

I have updated our base Array!T container with several high-performance methods O(N).

Do you have benchmarks showing how much this improves compilation times? Note that the dmd.root.array module is not a complete general purpose array implementation, it is only used for dmd to make bootstrapping easier. Hence additions to that module need to be motivated through dmd's use cases.

I am currently working on a profiler report to demonstrate the specific performance gains.

Regarding total compilation time: measuring a macro-level improvement right now is non-trivial. Since I extracted the refactoring of semantic3.d into a separate PR to keep this contribution minimal, these new methods are not yet being utilized in the compiler's hot paths. Consequently, any immediate gains are currently lost in the noise of a full build.

However, the report I am preparing compares the current O(N²) patterns often found in DMD (manual loops with remove) against the O(N) removeIf implementation. My goal is to show that while dmd.root.array is indeed specialized, adding these algorithmic primitives provides the necessary infrastructure to optimize.

I will post the benchmark results shortly.

@dkorpel
Copy link
Contributor

dkorpel commented Jan 1, 2026

Consequently, any immediate gains are currently lost in the noise of a full build.

It is indeed hard, but important to measure because expectations and reality can wildly differ when trying to optimize something.

I will post the benchmark results shortly.

Great! If you need any help benchmarking let me know.

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