C
C#4w ago
SWEETPONY

✅ How to deserialize as fast as possible?

var context = new MessageQueueBatchContext
{
Batch = [
new MessageQueueContext("Test Message"u8.ToArray()),
new MessageQueueContext("Test Message"u8.ToArray()),
new MessageQueueContext("Test Message"u8.ToArray())
]
};

class MessageQueueBatchContext
{
public required IReadOnlyList<MessageQueueContext> Batch { get; init; }
}

class MessageQueueContext(byte[] message)
{
public ReadOnlySpan<byte> Message => (ReadOnlySpan<byte>) message.AsSpan<byte>();
}
var context = new MessageQueueBatchContext
{
Batch = [
new MessageQueueContext("Test Message"u8.ToArray()),
new MessageQueueContext("Test Message"u8.ToArray()),
new MessageQueueContext("Test Message"u8.ToArray())
]
};

class MessageQueueBatchContext
{
public required IReadOnlyList<MessageQueueContext> Batch { get; init; }
}

class MessageQueueContext(byte[] message)
{
public ReadOnlySpan<byte> Message => (ReadOnlySpan<byte>) message.AsSpan<byte>();
}
I'm trying to get all messages from batch and deserialize it without multiple deserializer calls. I have following and it's doesn't work:
var messages = context.Batch.Select(b => b.Message.ToArray());
var s = JsonSerializer.Deserialize<List<string>>(messages);
var messages = context.Batch.Select(b => b.Message.ToArray());
var s = JsonSerializer.Deserialize<List<string>>(messages);
I know I can do this but it is not optimal:
var messages = context.Batch.Select(b => JsonSerializer.Deserialize<string>(b.Message));
var messages = context.Batch.Select(b => JsonSerializer.Deserialize<string>(b.Message));
18 Replies
jcotton42
jcotton424w ago
What makes it not optimal for you?
SWEETPONY
SWEETPONYOP4w ago
JsonSerializer.Deserialize in each message from batch
jcotton42
jcotton424w ago
But what's the issue with multiple calls to Deserialize?
SWEETPONY
SWEETPONYOP4w ago
why multiple if we can do it only 1 time like JsonSerializer.Deserialize<List<string>>
jcotton42
jcotton424w ago
Well that's not how Deserialize works, so again, why are you against multiple calls?
SWEETPONY
SWEETPONYOP4w ago
so how does it work?
jcotton42
jcotton424w ago
It takes a single string or stream and returns an object, not an array of strings. this feels very $xy
Angius
Angius4w ago
Using source-generated deserializer is likely to have more impact on performance tbh Though, ig, you are deserializing into plain strings
SWEETPONY
SWEETPONYOP4w ago
I just wanna serialize batch as fast as possible so thats why I'm here with my question This is my code:
using System.Text.Json;

var messageBytes = JsonSerializer.SerializeToUtf8Bytes("Test Message");
var context = new MessageQueueBatchContext
{
Batch = [
new MessageQueueContext(messageBytes),
new MessageQueueContext(messageBytes),
new MessageQueueContext(messageBytes)
]
};

var messages = context.Batch.Select(b => JsonSerializer.Deserialize<string>(b.Message));
Console.WriteLine(string.Join(',', messages));

class MessageQueueBatchContext
{
public required IReadOnlyList<MessageQueueContext> Batch { get; init; }
}

class MessageQueueContext(byte[] message)
{
public ReadOnlySpan<byte> Message => message.AsSpan();
}
using System.Text.Json;

var messageBytes = JsonSerializer.SerializeToUtf8Bytes("Test Message");
var context = new MessageQueueBatchContext
{
Batch = [
new MessageQueueContext(messageBytes),
new MessageQueueContext(messageBytes),
new MessageQueueContext(messageBytes)
]
};

var messages = context.Batch.Select(b => JsonSerializer.Deserialize<string>(b.Message));
Console.WriteLine(string.Join(',', messages));

class MessageQueueBatchContext
{
public required IReadOnlyList<MessageQueueContext> Batch { get; init; }
}

class MessageQueueContext(byte[] message)
{
public ReadOnlySpan<byte> Message => message.AsSpan();
}
jcotton42
jcotton424w ago
Using source generation to remove the runtime reflection overhead will net you far more perf than removing a handful a calls. Especially when you thus far have not articulated why having a couple extra calls is a problem.
SWEETPONY
SWEETPONYOP4w ago
I mean one call is always better than "couple extra calls"
jcotton42
jcotton424w ago
We're talking micro, maybe even nanoseconds difference here. The runtime of deserializing the JSON will utterly dwarf the overhead of a few extra method calls.
SWEETPONY
SWEETPONYOP4w ago
What should I use to deserialize faster?
Angius
Angius4w ago
There's been two mentions of source generation so far So, chances are, this
SWEETPONY
SWEETPONYOP4w ago
ah, okay, thanks
SWEETPONY
SWEETPONYOP4w ago
ookay u were right this is my benchmark:
using System.Text.Json;
using BenchmarkDotNet.Attributes;

namespace ConsoleApp534;

public class SerializationBenchmark
{
private byte[] messageBytes;
private MessageQueueBatchContext context;

[GlobalSetup]
public void Setup()
{
messageBytes = JsonSerializer.SerializeToUtf8Bytes("Test Message");
context = new MessageQueueBatchContext
{
Batch = Enumerable.Range(0, 512)
.Select(_ => new MessageQueueContext(messageBytes))
.ToList()
};
}

[Benchmark]
public void JsonSerializerSelect()
{
var messages = context.Batch.Select(b => JsonSerializer.Deserialize<string>(b.Message));
}

[Benchmark]
public void JsonSerializerSourceGen()
{
var messages = context.Batch.Select(b => JsonSerializer.Deserialize(b.Message, AppJsonContext.Default.String));
}

[Benchmark]
public void Utf8JsonReaderGetString()
{
var messages = context.Batch.Select(b =>
{
var reader = new Utf8JsonReader(b.Message);
reader.Read();
return reader.GetString();
});
}
}
using System.Text.Json;
using BenchmarkDotNet.Attributes;

namespace ConsoleApp534;

public class SerializationBenchmark
{
private byte[] messageBytes;
private MessageQueueBatchContext context;

[GlobalSetup]
public void Setup()
{
messageBytes = JsonSerializer.SerializeToUtf8Bytes("Test Message");
context = new MessageQueueBatchContext
{
Batch = Enumerable.Range(0, 512)
.Select(_ => new MessageQueueContext(messageBytes))
.ToList()
};
}

[Benchmark]
public void JsonSerializerSelect()
{
var messages = context.Batch.Select(b => JsonSerializer.Deserialize<string>(b.Message));
}

[Benchmark]
public void JsonSerializerSourceGen()
{
var messages = context.Batch.Select(b => JsonSerializer.Deserialize(b.Message, AppJsonContext.Default.String));
}

[Benchmark]
public void Utf8JsonReaderGetString()
{
var messages = context.Batch.Select(b =>
{
var reader = new Utf8JsonReader(b.Message);
reader.Read();
return reader.GetString();
});
}
}
| Method | Mean | Error | StdDev | |------------------------ |---------:|---------:|---------:| | JsonSerializerSelect | 10.12 ns | 0.155 ns | 0.145 ns | | JsonSerializerSourceGen | 10.35 ns | 0.203 ns | 0.190 ns | | Utf8JsonReaderGetString | 10.02 ns | 0.051 ns | 0.042 ns | pity

Did you find this page helpful?