Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Substring()似乎是这段代码的瓶颈_C#_Performance_Substring - Fatal编程技术网

C# Substring()似乎是这段代码的瓶颈

C# Substring()似乎是这段代码的瓶颈,c#,performance,substring,C#,Performance,Substring,导言 我有一个我很喜欢的算法,它是我很久以前做的,我一直在用新的编程语言、平台等编写和重新编写,作为某种基准。虽然我的主要编程语言是C#,但我只是简单地复制粘贴了代码并稍微修改了语法,用Java构建了它,发现它的运行速度快了1000倍 代码 这里有很多代码,但我只想介绍这段代码,这似乎是主要问题: for (int i = 0; i <= s1.Length; i++) { for (int j = i + 1; j <= s1.Length - i; j++) {

导言

我有一个我很喜欢的算法,它是我很久以前做的,我一直在用新的编程语言、平台等编写和重新编写,作为某种基准。虽然我的主要编程语言是C#,但我只是简单地复制粘贴了代码并稍微修改了语法,用Java构建了它,发现它的运行速度快了1000倍

代码

这里有很多代码,但我只想介绍这段代码,这似乎是主要问题:

for (int i = 0; i <= s1.Length; i++) 
{
    for (int j = i + 1; j <= s1.Length - i; j++)
    {
        string _s1 = s1.Substring(i, j);
        if (tree.hasLeaf(_s1))
         ...
探查器的屏幕截图片段

注意:这是用大小为300.000个字符的字符串s1测试的。由于某种原因,一百万个字符在C#中永远不会结束,而在Java中只需要0.75秒。。消耗的内存和垃圾收集的数量似乎并不表示内存问题。峰值约为400 MB,但考虑到巨大的后缀树,这似乎是正常的。也没有发现奇怪的垃圾收集模式


发行来源

在经历了一场持续两天三夜的光荣战斗(以及评论中的惊人想法和想法)之后,我终于解决了这个问题

我想为任何遇到类似问题的人发布一个答案,其中
string.Substring(I,j)
函数不是获取字符串子字符串的可接受解决方案,因为字符串太大,您无法负担
string.Substring(I,j)
(它必须复制,因为C#字符串是不可变的,无法绕过它)或
字符串。子字符串(i,j)
在同一个字符串上被调用了大量次(就像在我的嵌套for循环中),这给垃圾收集器带来了困难,或者在我的例子中两者都是

尝试

我尝试了许多建议,例如StringBuilder,在
不安全{}中使用Intptr封送进行非托管内存分配
阻塞,甚至创建IEnumerable,并通过引用返回给定位置内的字符。所有这些尝试最终都失败了,因为必须进行某种形式的数据连接,因为我无法轻松地逐个字符遍历我的树而不损及性能。如果有waY跨越一个数组内的多个内存地址,就像你将能够在C++中用一些指针算法……除了… (归功于@Ivan Stoev的评论)

解决方案

解决方案是使用
System.ReadOnlySpan
(由于字符串是不可变的,所以不能是
System.Span
),它允许我们读取现有数组中内存地址的子数组,而无需创建副本

发布的这段代码:

string _s1 = s1.Substring(i, j);
if (stree.has(_s1))
{
    score += j - i;
    longest = j - i;
}
已更改为以下内容:

if (stree.has(i, j))
{
    score += j - i;
    longest = j - i;
}
其中
stree.has()
现在接受两个整数(子字符串的位置和长度),并执行以下操作:

ReadOnlySpan<char> substr = s1.AsSpan(i, j);
ReadOnlySpan substr=s1.AsSpan(i,j);
请注意,
substr
变量实际上是对初始
s1
数组的字符子集的引用,而不是副本!(可以通过此函数访问
s1
变量)

请注意,在撰写本文时,我使用的是C#7.2和.NET Framework 4.6.1,这意味着要获得Span功能,我必须进入Project>Manage NuGet Packages,勾选“Include prerelease”复选框,浏览System.Memory并安装它


重新运行初始测试(在长度为100万字符的字符串上,即1MB)速度从2分钟以上提高(2分钟后我放弃等待)大约86毫秒!!

String
在Java中也是不可变的。你尝试过
StringBuilder
吗?我猜你有内存问题。你看过了吗?Java中的八个内核中有七个可能用于垃圾收集你的子字符串:)哈哈,可能就是它了……)。你知道如何在C#中不需要一直复制就可以得到我的子字符串吗?我不能只使用C++中的指针算法,直到C语言得到<代码> Strue/Cube >,因为其他评论者指出,在<代码> Stre等方法中,只使用<代码>(String,StistCurnand,EndiDeX) >。在方法内部使用字符串索引器(
s[i]
),它返回
char
w/o分配。可以作为创建Span的一部分进行切片:
s1。AsSpan(i,j)
,应该快一点?可能是因为我不知道Span是如何实现的。它看起来并不是更快,但直觉上认为它是。。至少我这么认为。我将编辑我的帖子并使用您的建议,因为如果您感兴趣,这可能是使用span@benadam有关span的更多信息的预期方式。(仅为完整起见)
ReadOnlySpan<char> substr = s1.AsSpan(i, j);