Skip to content

Conversation

@TimothyMakkison
Copy link
Contributor

@TimothyMakkison TimothyMakkison commented Jan 4, 2026

Uses ValueListBuilder and ValueStringBuilder to drastically reduce allocations in TestIdentifierService

  • Note that ValueListBuilder has a lot of potential in other areas of TUnit; I see a lot of temporary Lists in use.
  • typeSb could be a VLS but it's used so infrequently I don't see any point - Happy to change this.
    • Edit switched this to use VLS as it may see some use if someone uses generic tests.
  • removes a lot of intermediary string allocations and ArrayPool usage

Bef ore

image

After

image

@thomhurst
Copy link
Owner

Summary

Optimizes TestIdentifierService by replacing StringBuilder and ArrayPool with ValueStringBuilder and ValueListBuilder to reduce heap allocations during test ID generation.

Critical Issues

None found ✅

Suggestions

  1. Consider stack allocation size for generic type names (TUnit.Engine/Services/TestIdentifierService.cs:108)

    • The typeVsb uses stackalloc char[128] which may be insufficient for deeply nested generic types with long namespace names
    • Suggestion: Consider increasing to 256 chars or add a comment about the size choice
  2. Minor: ValueListBuilder initial capacity (TUnit.Engine/Services/TestIdentifierService.cs:91)

    • The initial capacity [null, null, null, null] (4 elements) may be too small for deeply nested types
    • However, this is likely rare in practice and the builder will grow automatically, so this is very minor

Verdict

APPROVE - No critical issues

This is an excellent performance optimization that follows TUnit's Rule 4 (Performance First). The implementation:

  • Eliminates heap allocations in a hot path (test discovery)
  • Properly disposes of ref structs
  • Maintains the same logic while improving efficiency
  • Is AOT-compatible

The benchmarks show significant improvement (124 B → 56 B allocated). Great work!

@thomhurst
Copy link
Owner

This seems to have broken execution for a bunch of tests. I think using a custom collection is quite risky too. Maybe we should leave this one.

@TimothyMakkison
Copy link
Contributor Author

TimothyMakkison commented Jan 7, 2026

This seems to have broken execution for a bunch of tests.

What are the errors? I wouldn't be surprised if I messed up the logic without any tests: 😊

I think using a custom collection is quite risky too. Maybe we should leave this one.

Fair enough, I had a lot of plans to use these but I can adapt. For what it's worth the collections are both internally used by Microsoft in the .Net runtime and IMO are easier to use than ArrayPools

@thomhurst
Copy link
Owner

How come they don't add it into the core library?

I haven't investigated properly, but it's expected things like 84 tests in a run and only getting 13. Click on the Ubuntu pipeline failures on the pr checks

@TimothyMakkison
Copy link
Contributor Author

TimothyMakkison commented Jan 7, 2026

The failed test are mostly TUnit.Engine so I probably messed up some logic when rewriting this method. Going to be a pain debugging this without local tests 😅 Not sure why TUnit.TestProject.Fail4 is failing.

How come they don't add it into the core library?

There is a proposal for this but it's blocked until they add a safety tool. They are worried that people will accidentally misuse it by; copying the ValueStringBuilder (instead of passing by ref), and then accidentally using an array that was returned to the ArrayPool. Andrew lock has a better explanation on his blog.

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