Strings and the CLR – A Special Relationship
mattwarren.org
mattwarren.org
There are many things that aren't possible in C# but can be expressed in pure IL. Sigil [0] is an IL generator used in the high-performance JSON serializer Jil [1], which takes advantage of this.
Are you saying that you could exactly replicate a C# System.String using IL, or am I mis-understanding?
However, at the instruction level you can do some pretty awesome stuff. This slice[1] library is able to verifiably and safely grab a slice of data out of any arbitrary structure by using a MSIL feature that was invented for C++/CLR. The call site[2] is not unsafe.
[1]: https://github.com/joeduffy/slice.net/blob/master/src/PtrUti... [2]: https://github.com/joeduffy/slice.net/blob/master/src/Slice....
[1]: https://github.com/dotnet/corefxlab/tree/master/src/System.S...
BTW there is a typo: "passed in my the calling code" should probably be "passed in by the calling code".
I wonder if the new C# 6 string interpolation also uses string builder like format does. e.g.
$"Name = {name}, hours = {hours:hh}"
https://msdn.microsoft.com/en-us/library/Dn961160.aspxThis post seems to suggest it uses concatenation, which may be bad for performance in some situations due to the immutability.
https://weblogs.asp.net/bleroy/c-6-string-interpolation-is-n...
> BTW there is a typo: "passed in my the calling code" should probably be "passed in by the calling code".
Thanks for spotting that, I'll fix it in a bit
One example from the article that may be a little off is getting the length of string. It looks like the call to string.get_Length is probably replaced with a JIT intrinsic[2]. The importer then turns that intrinsic call into a GT_ARR_LENGTH[3] node that is eventually lowered into something like[4]:
*(stringAddr + offsetOfStringLengthField)
It's a bit more complicated than that, as there are several references to GT_ARR_LENGTH in various optimization passes.[0]: https://github.com/dotnet/corert/ [1]: https://github.com/dotnet/corert/tree/master/src/Runtime.Bas... [2]: https://github.com/dotnet/coreclr/blob/a123c3972c447c8bcfa18... [3]: (warning, big file) https://github.com/dotnet/coreclr/blob/a123c3972c447c8bcfa18... [4]: (warning, big file) https://github.com/dotnet/coreclr/blob/a123c3972c447c8bcfa18...
Yep, that's a large part of the motivation for me writing this post, although after looking at the source, I then went back to WinDBG and the Microsoft Symbol Server, which I could've done before it was open sourced!!
Thanks for the clarification on what the JIT is doing to optimise the string length. So far I've not managed to find the time to look at it's source, it took me long enough to find my way round CoreCLR!
In the post, I sort-of hand-waved about what was happening there, it's nice to have some more concrete details.
It looks like if you use the automatic BSTR marshaler it creates a copy[1], though the CLR is so big it's hard for me to say that with confidence. Taking the address of a string with the fixed statement in C# of course won't allocate and that pointer can be passed to APIs that expected Unicode strings.
[0]: https://github.com/dotnet/coreclr/blob/a123c3972c447c8bcfa18... [1]: https://github.com/dotnet/coreclr/blob/a123c3972c447c8bcfa18...
also the B in BSTR is for Basic!
https://blogs.msdn.microsoft.com/ericlippert/2003/09/16/eric...
That could be a huge efficiency gain because of cache misses.
https://www.gnu.org/software/smalltalk/manual-base/html_node...
https://www.gnu.org/software/smalltalk/manual/html_node/Insi...
Although see the footnote - "This is not always true for other Smalltalk implementations, who don't allow instance variables in variableByteSubclasses and variableWordSubclasses". So, in some implementations, you can have either normal fields or array slots, but not both. That said, i think if you have a base class with fields, you can always make a subclass with slots.
There are two main limitations to them:
* by default most generic code can not operate over them, generics require the special `?Sized` bound to allow DSTs (essentially generics have a default Sized generic bound)
* Rust doesn't currently support dynamic stack allocation so DSTs can't be stack-allocated (https://github.com/rust-lang/rfcs/issues/618)
Windows interop, and 8/16 conversions, would obviously be an expense, but ~half the storage requirements of UTF-16 have to represent a substantial CPU/RAM saving.
Guess it's too much cruft and complexity to introduce into .NET now, but who knows? https://twitter.com/terrajobst/status/717935598904807424
Interestingly enough Java just implemented compact strings that can be ISO-8859-1/Latin-1 (one byte per character) or as UTF-16 (two bytes per character). See http://openjdk.java.net/jeps/254 and https://www.infoq.com/news/2016/02/compact-strings-Java-JDK9 for more info.
Can one extend the CLR (modulalary) and access via: extern and [MethodImplAttribute(MethodImplOptions.InternalCall)]?
Native UTF-8 is an obvious example but the scope could be far greater in scope such as native interop for a hardware device (IoT), funky filesystems or hypervisors?
Those [MethodImplAttribute(MethodImplOptions.InternalCall)] calls are still within the CoreCLR codebase, they're just from the managed part -> un-managed part.