Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can `ReadOnlyMemory<char>` be used as a universal replacement for `string`?

Tags:

c#

memory

The way I understand things, obtaining a ReadOnlyMemory<char> from a string is very cheap, whereas obtaining a string from a ReadOnlyMemory<char> tends to involve allocation and copying, so it is not very cheap.

Luckily, many CLR methods that used to accept string have received overloads accepting ReadOnlySpan<char>, which can be cheaply obtained from ReadOnlyMemory<char>, so in many cases we can reap the performance benefits of ReadOnlyMemory<char> without having to replace all of our strings with ReadOnlyMemory<char>. (We mostly just have to remember to replace string.Substring() with string.AsSpan().Slice() when invoking methods that have span-accepting overloads.)

However, there are many cases where the performance benefits of ReadOnlyMemory<char> cannot be realized. For example, if I have a dictionary with string keys, I can only pass string to it, which means that when I want to do a lookup using a part of a string as key, I have no option but to use string.Substring() to obtain the key to lookup, which is expensive.

This situation seems to be hinting that maximum performance could be realized if all instances of string were replaced with ReadOnlyMemory<char> throughout the solution. If my dictionary was using ReadOnlyMemory<char> as key, (or a readonly struct which aggregates both a ReadOnlyMemory<char> and its hashcode, so that the hashcode does not have to be re-computed all the time,) then I would be able to do efficient lookups using either string or ReadOnlyMemory<char>.

So, my questions:

Has anyone attempted replacing all strings with ReadOnlyMemory<char>s in a medium-scale (not small) solution?

What are the problems one is likely to encounter when performing such a conversion?

Or am I missing something and it is in fact possible to somehow use substrings (as keys for dictionary lookups etc.) without extra allocations?

Clarification:

I am not talking about using ReadOnlySpan<char> to replace string with, because a string can live everywhere, whereas a ReadOnlySpan<char> can only live in the stack, since it is a ref struct. On the other hand, ReadOnlyMemory<char> is a regular struct, not a ref struct, so it can live in all the places where a string can live.

like image 426
Mike Nakis Avatar asked Dec 30 '25 09:12

Mike Nakis


1 Answers

Though I haven't done this kind of replacement before, but I can think of an obvious problem. string is immutable which is different from readonly. You must know that dictionary relys on the GetHashCode method by default. The implementation of string's GetHashCode method is based on the content, but ReadOnlyMemory is based on the fragment it references.

A simple counterexample:

var chars = "abc".ToCharArray();
var m = chars.AsMemory();
Console.WriteLine(m.GetHashCode());
chars[0] = 'n';
Console.WriteLine(m.GetHashCode()); // no change
like image 142
shingo Avatar answered Jan 01 '26 04:01

shingo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!