Skip to content

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

Description

@MaiLinhP

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):

{
    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<byte> via Utf8Formatter).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

emitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions