.NET与Java之间子串操作性能的比较

获取字符串的子字符串是一种非常常见的字符串操作操作,但我听说Java和.NET平台之间的性能/实现可能存在很大差异。 具体来说,我听说在Java中, java.lang.Stringsubstring提供了常量时间操作,但在.NET中, System.String提供了线性性能Substring

这些真的是这样吗? 可以在文档/源代码等中确认吗? 此实现是特定的,还是由语言和/或平台指定的? 每种方法的优缺点是什么? 一个人从一个平台迁移到另一个平台应该寻求什么来避免陷入任何性能陷阱?

在.NET中, Substring是O(n)而不是Java的O(1)。 这是因为在.NET中,String对象包含所有实际的字符数据本身1 – 因此获取子字符串涉及复制新子字符串中的所有数据。 在Java中, substring可以创建一个引用原始char数组的新对象,具有不同的起始索引和长度。

每种方法都有利弊:

  • .NET的方法具有更好的缓存一致性,创建更少的对象2 ,并避免一个小子串阻止非常大的char[]被垃圾收集的情况。 我相信在某些情况下,内部也可以使互操作变得非常简单。
  • Java的方法使得子串非常有效,也可能是其他一些操作

我的弦乐文章中有更多细节。

至于避免性能缺陷的一般问题,我认为我应该准备好剪切和粘贴的固定答案:确保您的架构高效,并以最易读的方式实现它。 衡量性能,并优化您发现瓶颈的位置。


1顺便说一句,这使得string非常特殊 – 它是唯一的非数组类型,其内存占用量在同一CLR中实例变化。

2对于小弦乐来说,这是一个很大的胜利。 一个对象的所有开销都很糟糕,但是当涉及到额外的数组时,单字符字符串在Java中可能需要大约36个字节。 (这是一个“空中手指”号码 – 我不记得确切的对象开销。它还取决于你正在使用的VM。)

使用reflection器这是你从Substring(Int32,Int32)得到的

 [SecuritySafeCritical, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public string Substring(int startIndex, int length) { return this.InternalSubStringWithChecks(startIndex, length, false); } 

如果你继续进去,最后一次通话是为了

 internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount) 

使用指针复制字符。 完整的代码实际上看起来很大,但在运行它并对其进行基准测试之前,您不会看到它有多快或多慢。

根据这不是真的: C#Substring

这真的取决于你的工作量。 如果您正在循环并执行大量子字符串调用,那么您可能会遇到问题。 对于你所指的SOpost,我怀疑它会成为一个问题。 然而,凭借这种态度,你总能在“千纸砍减”的情况下结束。 在您提到的SOpost中,我们有以下内容:

 String after = before.Substring(0, 1).ToUpper() + before.Substring(1); 

假设编译器没有做一些疯狂的优化,这将创建至少四个新字符串(2 Substring调用,一个ToUpper调用和连接)。 子串完全按照您的预期实现(字符串复制),但上面分配的三个字符串很快就会变成垃圾。 做很多事情会造成不必要的记忆压力。 我说“不必要”,因为你可以提出一个更经济的解决方案,只需要更多的时间投资。

最后,探查器是你最好的朋友:)