如何从C#中有效超过300万个字符的字符串中获取100万个子字符串?我编写了一个程序,涉及从300万个字符的字符串中读取长度为100的随机DNA读取(随机位置的子串)。有100万这样的读数。目前我运行一个运行100万次的while循环,并从具有300万字符的字符串中读取100个字符长度的子字符串。这需要很长时间。我能做些什么来更快地完成这个?
继承我的代码, len 是原始字符串的长度,在这种情况下为300万,它可能低至50,这就是为什么在while循环中检查。
while(i < 1000000 && len-100> 0) //len is 3000000
{
int randomPos = _random.Next()%(len - ReadLength);
readString += all.Substring(randomPos, ReadLength) + Environment.NewLine;
i++;
}
答案 0 :(得分:2)
使用StringBuilder来组合字符串将使处理量增加600倍(因为它可以避免每次附加到字符串时重复创建对象。
在循环之前(初始化容量避免在StringBuilder中重新创建支持数组):
StringBuilder sb = new StringBuilder(1000000 * ReadLength);
循环:
sb.Append(all.Substring(randomPos, ReadLength) + Environment.NewLine);
循环后:
readString = sb.ToString();
使用char数组而不是字符串来提取值,因为您在调用Substring()时避免了对象创建,因此可以进一步提高30%:
循环之前:
char[] chars = all.ToCharArray();
循环:
sb.Append(chars, randomPos, ReadLength);
sb.AppendLine();
编辑(最终版本不使用StringBuilder并在300毫秒内执行):
char[] chars = all.ToCharArray();
var iterations = 1000000;
char[] results = new char[iterations * (ReadLength + 1)];
GetRandomStrings(len, iterations, ReadLength, chars, results, 0);
string s = new string(results);
private static void GetRandomStrings(int len, int iterations, int ReadLength, char[] chars, char[] result, int resultIndex)
{
Random random = new Random();
int i = 0, index = resultIndex;
while (i < iterations && len - 100 > 0) //len is 3000000
{
var i1 = len - ReadLength;
int randomPos = random.Next() % i1;
Array.Copy(chars, randomPos, result, index, ReadLength);
index += ReadLength;
result[index] = Environment.NewLine[0];
index++;
i++;
}
}
答案 1 :(得分:1)
我认为会有更好的解决方案,但.NET StringBuilder类实例比String类实例更快,因为它以Stream形式处理数据。
您可以分割数据并使用.NET任务并行库进行多线程和并行化
编辑:为循环中的变量分配固定值以避免重新计算;
int x = len-100
int y = len-ReadLength
使用
StringBuilder readString= new StringBuilder(ReadLength * numberOfSubStrings);
readString.AppendLine(all.Substring(randomPos, ReadLength));
对于Parallelism,您应该将输入分成几部分。然后在单独的线程中对这些操作运行这些操作。然后结合结果。
重要:正如我之前的经验表明,这些操作使用.NET v2.0而不是v4.0运行得更快,因此您应该更改项目目标框架版本;但是你不能在.NET v2.0中使用任务并行库,所以你应该像老式的那样使用多线程
Thread newThread ......
答案 2 :(得分:0)
编辑:我放弃了使用memcpy的想法,我认为结果非常好。 我已经将一个3米长的字符串分成了30k字符串,每行长度为100,长度为43毫秒。
private static unsafe string[] Scan(string hugeString, int subStringSize)
{
var results = new string[hugeString.Length / subStringSize];
var gcHandle = GCHandle.Alloc(hugeString, GCHandleType.Pinned);
var currAddress = (char*)gcHandle.AddrOfPinnedObject();
for (var i = 0; i < results.Length; i++)
{
results[i] = new string(currAddress, 0, subStringSize);
currAddress += subStringSize;
}
return results;
}
要使用问题所示案例的方法:
const int size = 3000000;
const int subSize = 100;
var stringBuilder = new StringBuilder(size);
var random = new Random();
for (var i = 0; i < size; i++)
{
stringBuilder.Append((char)random.Next(30, 80));
}
var hugeString = stringBuilder.ToString();
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++)
{
var strings = Scan(hugeString, subSize);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds / 1000); // 43
答案 3 :(得分:0)
很长时间了?它应该不会那么久。
var file = new StreamReader(@"E:\Temp\temp.txt");
var s = file.ReadToEnd();
var r = new Random();
var sw = new Stopwatch();
sw.Start();
var range = Enumerable.Range(0,1000000);
var results = range.Select( i => s.Substring(r.Next(s.Length - 100),100)).ToList();
sw.Stop();
sw.ElapsedMilliseconds.Dump();
s.Length.Dump();
所以在我的机器上结果是807ms,字符串是4,055,442个字符。
编辑:我刚注意到你想要一个字符串作为结果,所以我的上述解决方案只是改为......
var results = string.Join(Environment.NewLine,range.Select( i => s.Substring(r.Next(s.Length - 100),100)).ToArray());
并且增加了大约100毫秒,所以仍然不到一秒钟。