Skip to content

[Xml Serialization] Possible optimization when writing primitive objects #67222

@TrayanZapryanov

Description

@TrayanZapryanov

This is follow up of 67005.
For example if I want to serialize array of decimals - all of them are written into string and then to the underlying XmlWriter.
Benchmark used to test :

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml.Serialization;
using BenchmarkDotNet.Attributes;

namespace SimpleBenchmarkConsoleApp
{
	[MemoryDiagnoser]
	public class ReflectionXmlSerializerBenchmarks
	{
		private readonly XmlSerializer _serializer;
		private readonly decimal[] _data;
		private readonly MemoryStream _stream;

		private const string SerializationModeSetterName = "set_Mode";

		static ReflectionXmlSerializerBenchmarks()
		{
			MethodInfo method = typeof(XmlSerializer).GetMethod(SerializationModeSetterName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
			if (method == null) throw new Exception($"No method named {SerializationModeSetterName}");
			method.Invoke(null, new object[] { 1 });
		}
		public ReflectionXmlSerializerBenchmarks()
		{
			_serializer = new XmlSerializer(typeof(decimal[]));
			_stream = new MemoryStream();
			_data = Enumerable.Repeat(12345.6789m, 200).ToArray();

			_serializer.Serialize(_stream, _data);
			_stream.Position = 0;
		}

		[Benchmark]
		public decimal Serialize()
		{
			_serializer.Serialize(_stream, _data);
			var result = _stream.Position;
			_stream.Position = 0;

			return result;
		}
	}
}

And this result into following allocations:
image

Idea for optimization:
Extend XmlRawWriter with additional virtual WriteSpan method which fallbacks to WriteString, but in some classes can be overridden in more performant way. For example XmlUtf8RawTextWriter already has a buffer of bytes and there we can override it like this :

internal override unsafe void WriteSpan(ReadOnlySpan<char> value)
{
    Debug.Assert(value.Length != 0);
    fixed (char* pSrc = value)
    {
        char* pSrcEnd = pSrc + value.Length;
        if (_inAttributeValue)
        {
            WriteAttributeTextBlock(pSrc, pSrcEnd);
        }
        else
        {
            WriteElementTextBlock(pSrc, pSrcEnd);
        }
    }
}

I will add a draft PR , just to visualize the idea and a sample how decimals can benefit from it.

Metadata

Metadata

Labels

area-Serializationin-prThere is an active PR which will close this issue when it is mergedtenet-performancePerformance related issue

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions