CLR字符串索引器是如何工作的



SSCLI声明中某些C++代码中隐藏的注释,指的是String的非托管内部实现。Chars属性:

实际上并没有使用这种方法。JIT将为字符串类上的索引器方法生成代码。

所以。。。这是什么神奇的代码?我理解抖动的全部意义在于,它们在不同的情况下产生不同的代码。但至少,对于现代x64 Windows 7+平台来说,抖动是如何实现的?还是那真的是秘制酱汁?

其他详细信息

不久前,我正在寻找最快的方法来迭代C#中字符串中的各个字符。它是最快的方法,不需要使用不安全的代码或复制内容(通过ToCharArray())是内置的字符串索引器,它实际上是对string.Chars属性的调用。就在我的原件里我问了一个问题,是否有人了解索引器的实际工作方式,但尽管Skeet和利珀特,我没有得到任何回应。所以我决定自己深入研究:

停止1:mscorlib

通过使用ildasm检查mscorlib.dll,我们可以看到String::get_Chars(int32 index)只是一个internalcall指针(加上一个属性):

.method public hidebysig specialname instance char 
        get_Chars(int32 index) cil managed internalcall
{
  .custom instance void System.Security.SecuritySafeCriticalAttribute::.ctor() = ( 01 00 00 00 ) 
} // end of method String::get_Chars

正如MethodImplOptions枚举的文档中所指出的,"内部调用是对在公共语言运行库中实现的方法的调用。"2004 MSDN Magazine的一篇文章SO的一篇帖子都表明,internalcall名称到非托管实现的映射可以在Shared Source CLI的ecall.cpp中找到

停止2:ecapp.cpp

搜索ecall.cpp的在线副本可以发现get_Chars是由COMString::GetCharAt:实现的

FCIntrinsic("get_Chars", COMString::GetCharAt, CORINFO_INTRINSIC_StringGetChar)

停止3:comstring.cpp

comstring.cpp确实包含GetCharAt的实现,从第1219行开始。除了,它前面有这样的评论:

/*==================================GETCHARAT===================================
**Returns the character at position index.  Thows IndexOutOfRangeException as
**appropriate.
**This method is not actually used. JIT will generate code for indexer method on string class.
**
==============================================================================*/

首先,请参阅Hans-Passant对关键位的评论。

在早期的.NET(CLR 1和2)中,CLR对StringStringBuilder类型有相当大的特殊支持。事实上,这两种类型的工作非常紧密,以至于StringBuilder.ToString不会将实际字符复制到任何地方,字符串索引器仍然使用特殊的抖动支持从同一内存位置获取字符。我认为String.Chars的抖动支持最初是必要的,以避免通过堆栈传递索引整数,但从那时起抖动似乎有所改善。

.NET4提供了StringBuilder(ropes)的不同实现,它不再与处理String的方式挂钩。(它必须在ToString期间复制,但附加速度要快得多。)在这些更改之后,

  • StringBuilder索引器在大型字符串上被戏剧性地减慢到O(logn)。请参见此处。它从不内联,甚至在短字符串上也不内联
  • String索引器仍然使用(未公开的)特殊抖动支持。我希望这一个基本上被内联到移位、加法和内存提取中,或者比最近的循环更快的东西

最新更新