microsoft / microsoft/typespec

Generated collection serializers allocate per-element when computing indexed patch paths

Open
#11,934 0 comments 2 reactions 2 assignees Claimed by @jorgerangel-msft View on GitHub
emitter:client:csharp
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.