microsoft / microsoft/typespec
Generated collection serializers allocate per-element when computing indexed patch paths
- Dominant language
- Java
- Stars
- 5.9k
- Forks
- 394
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 104
Description
The `http-client-csharp` emitter generates array-serialization loops that build a JSON-patch path for every element on the normal serialization path, even when `Patch` is empty. In `OpenAIEmbeddingCollection.Serialization.cs` (and identically in `ResponseItemCollectionPage`, `ContainerCollectionPage`, `InternalConversationItemCollection`, and other generated collections):
```for (int i = 0; i < Items.Count; i++)
{
if (Patch.IsRemoved(Encoding.UTF8.GetBytes($"$.data[{i}]")) || Items[i] != null && Items[i].Patch.IsRemoved("$"u8))
{
continue;
}
writer.WriteObjectValue(Items[i], options);
}
```
`Encoding.UTF8.GetBytes($"$.data[{i}]")` runs for every element on every `WriteTo`, allocating twice per item (the interpolated `string` and the `byte[]`) just to probe `Patch.IsRemoved`. In the common unpatched case this is entirely wasted—the enclosing else is only reached when Patch doesn't contain `"$.data"`, so `IsRemoved` returns false every time—yielding O(n) avoidable allocations per serialization, amplified across all the generated collection types sharing this shape. The same pattern also appears in the generated `ActiveItems()` helper.
Suggested fix: Skip the per-element `IsRemoved` check when `Patch` has no relevant entries, and/or replace the `string` + `Encoding.UTF8.GetBytes` allocation with an allocation-free UTF-8 path builder (write `$.data[{i}]` into a stack/pooled `Span` via `Utf8Formatter`).
Contributor guide
Assessment
This issue has not been assessed yet.