-
-
Notifications
You must be signed in to change notification settings - Fork 108
perf: reduce allocations in generated code #4236
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
Conversation
8b1f599 to
5503321
Compare
SummaryThis PR modifies the AssertionExtensionGenerator to reduce allocations in generated extension methods by avoiding string concatenation, array allocations, and LINQ operations. Critical Issues1. Null handling removed - Breaking Change
|
Where are these? |
|
Hmm... I don't know why that's not working. That should source generate project locations on your machine |
5503321 to
ceb9742
Compare
|
Okay, I've reverted back to using I have a lot of ideas for how to improve the path with better code generation, but I suspect it is low hanging fruit.
Running git clean and reset hasn't helped, running this on Windows 11 and I have |
|
If any snapshots need updating I can pull the branch and do it for ya. I'm not sure the assertions have snapshots though. It's mainly TUnit.Core.SourceGenerator that uses a lot of snapshots |
|
We could probably also optimise for parameter counts of 2 and 3. I think that'd cover 99% of scenarios, and avoid the LINQ/enumerable allocations |
Thanks, 🙏 I've been trying to fix the issue but I might have to ask you to do that.
Okay, these were my ideas Custom method// collection initialiser uses a custom struct coerced into a span, no allocations
// should work for all versions of .NET, might break depending on C# version iirc not an issue
// Don't know where the extension method should live
// If span is an issue the method could have overloads for each number of parameters with a params fallback
source.Context.ExpressionBuilder.JoinNonNull([boolValueExpression, intValueExpression, stringValueExpression]);
public static void JoinNonNull(this StringBuilder sb, string separator, ReadOnlySpan<string?> vals); // adds non null value with separatorChain ifsvar added = false;
if(boolValueExpression != null)
{
source.Context.ExpressionBuilder.Append(added ? ", " : "");
source.Context.ExpressionBuilder.Append(boolValueExpression);
added = true;
}
if(intValueExpression!= null)
{
source.Context.ExpressionBuilder.Append(added ? ", " : "");
source.Context.ExpressionBuilder.Append(intValueExpression);
added = true;
}
// etcForeachReadOnlySpan<string?> expressions = [boolValueExpression, intValueExpression, stringValueExpression];
var added = false;
foreach (var expression in expressions)
{
if(expression == null)
{
continue;
}
source.Context.ExpressionBuilder.Append(added ? ", " : "");
source.Context.ExpressionBuilder.Append(expression);
addedd = true;
}All these solutions won't allocate and likely have the same performance
|
|
Okay, opted for if statement generation, as it doesn't need
I don't see how using the local variable
Yes please 🙏 I still can't get the tests to run. |
Modify generated code to avoid allocations.
string[]allocation,ArrayWhereIteratorand string concatenation.TUnithave tests for generated assertion code? Most of the tests forTUnitusually fail for me so I don't bother using them 😅Note that there is a lot of room for improvement with the
additionalParams.Length > 0case. I can think of a couple of approaches if you'd like to try themBefore
After
Note