在 C# 代码上使用 GPU/TPL 来加快速度,耗时 40 分钟
use GPU/TPL on C# code to speed up things, taking 40 minutes
我想对一个文本文件执行一些计算,该文件每行有 1 个数字 "0,1",几乎有 100 万行。
我想检查一个序列在整个文件中存在多少次,并根据sequence length
生成一个序列,例如我的文件是:
01100101011....up to 1 milion (each number on a new line)
代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
public class Program
{
static void Main(string[] args)
{
Stopwatch time = new Stopwatch();
time.Start();
try
{
// I have hard coded fileName and Sequence Length that i am taking from user
string data = "", fileName = "10.txt"; // this file has almost 1 Million records
int first = 0, last = 0;
// reads data and make a string of that data
// which means "data" = "1001011001010100101 .... upto 1 million"
data = string.Join("", File.ReadAllLines(fileName));
last = Convert.ToInt32("15"); // sequence length
int l = data.Length; // calculates it one time so that dont have to do it everytime
//so why i create List is because sometime Array dont get fully used to its length
// and get Null values at the end
List<string> dataList = new List<string>();
while (first + last < l+1)
{
dataList.Add((data.Substring(first, last)));
first++;
}
// converts list to Array so array will have values and no Null
// and will use Array.FindAll() later
string[] dataArray = dataList.ToArray(), value;
// get rready a file to start writing on
StreamWriter sw = new StreamWriter(fileName.Substring(0, fileName.Length - 4) + "Results.txt");
//THIS IS THE PART THATS TAKING around 40 minutes
for (int j = 0; j < dataArray.Length; j++)
{
// finds a value in whole array and make array of that finding
value = Array.FindAll(dataArray, str => str.Equals(dataArray[j]));
// value.Length means the count of the Number in the whole array
sw.WriteLine(value.Length);
}
sw.Close();
time.Stop();
Console.WriteLine("Time : " + time.Elapsed);
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Exception " + ex.StackTrace);
Console.ReadLine();
}
}
}
我设置了一个 sequence length = 3
,现在我的程序做了一个数组:
dataArray = {"011" , "110" , "100" , "001" , "010" , "101" , "011"}
通过使用 String.Substring()
。现在我只想计算数组元素的频率。
结果 .txt 文件中的数据
011 - 2
110 - 0
100 - 0
001 - 0
010 - 0
101 - 0
011 - 2
现在看起来很简单其实不然,我无法转换它int
因为它是一个序列我不想丢失序列前面的零
现在我的程序必须循环 1 百万(每个元素)X 1 百万(与数组的每个元素比较)= 1 万亿 次。这需要将近 40 分钟。我想知道我怎样才能让它变得更快,Parallel.For,TPL 我不知道他们如何使用它们。因为它应该在几秒钟内完成。
我的系统规格
32 GB RAM
i7- 5820k 3.30 ghz
64 bit
2x nvidia gtx 970
如果我正确理解你的代码和问题,你需要 "slide a window"(长度为 N,在你的原始代码中为 last
)文本,并计算每个子字符串的次数存在于文中。
如果这是正确的,下面的代码在 million-character 文件上会在 0.292 秒左右完成,而且你根本不需要并行或 GPU。
这里的想法是在我们将 window 滑过文本时将块数统计为 Dictionary
。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
public class Program
{
static Dictionary<string, int> CountChunks(string data, int chunkLength)
{
var chunkCounts = new Dictionary<string, int>();
var l = data.Length;
for (var i = 0; i < l - chunkLength; i++)
{
var chunk = data.Substring(i, chunkLength);
int count = 0;
chunkCounts.TryGetValue(chunk, out count);
chunkCounts[chunk] = count + 1;
}
return chunkCounts;
}
static void Main(string[] args)
{
var time = new Stopwatch();
time.Start();
var fileName = "10.txt";
var data = string.Join("", File.ReadAllText(fileName));
var chunkCounts = CountChunks(data, 15);
using (var sw = new StreamWriter(fileName.Substring(0, fileName.Length - 4) + "Results.txt"))
{
foreach (var pair in chunkCounts)
{
sw.WriteLine($"{pair.Key} - {pair.Value}");
}
}
time.Stop();
Console.WriteLine("Time : " + time.Elapsed);
}
}
输出 10Results.txt
看起来像
011100000111100 - 34
111000001111000 - 37
110000011110001 - 27
100000111100010 - 28
000001111000101 - 37
000011110001010 - 36
000111100010100 - 44
001111000101001 - 35
011110001010011 - 41
111100010100110 - 42
等等
编辑: 这是等效的 Python 程序。稍微慢一点,大约 0.9 秒。
import time
from collections import Counter
t0 = time.time()
c = Counter()
data = ''.join(l for l in open('10.txt'))
l = 15
for i in range(0, len(data) - l):
c[data[i : i + l]] += 1
with open('10Results2.txt', 'w') as outf:
for key, value in c.items():
print(f'{key} - {value}', file=outf)
print(time.time() - t0)
For 循环会给您带来糟糕的性能,因为它必须循环执行一百万个字符串比较。
我建议使用字典而不是列表来将序列存储为键并计为值。
与 while/for 循环相比,它应该会给您带来更好的性能。
您需要做的就是从性能角度稍微调整一下,甚至可能不需要利用 GPU/TLP 运行时,除非这是您的唯一目的。
下面的东西应该让你去。
string keyString = string.Empty;
Dictionary<string,int> dataList = new Dictionary<string,int>;
while (first + last < l+1)
{
keyString = data.Substring(first, last);
if(dataList.ContainsKey(keyString)
{
dataList[keyString] = dataList[keyString] + 1;
}
else
{
dataList.Add(keyString,1);
}
first++;
}
您需要的其余代码是打印这本词典。
我想对一个文本文件执行一些计算,该文件每行有 1 个数字 "0,1",几乎有 100 万行。
我想检查一个序列在整个文件中存在多少次,并根据sequence length
生成一个序列,例如我的文件是:
01100101011....up to 1 milion (each number on a new line)
代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
public class Program
{
static void Main(string[] args)
{
Stopwatch time = new Stopwatch();
time.Start();
try
{
// I have hard coded fileName and Sequence Length that i am taking from user
string data = "", fileName = "10.txt"; // this file has almost 1 Million records
int first = 0, last = 0;
// reads data and make a string of that data
// which means "data" = "1001011001010100101 .... upto 1 million"
data = string.Join("", File.ReadAllLines(fileName));
last = Convert.ToInt32("15"); // sequence length
int l = data.Length; // calculates it one time so that dont have to do it everytime
//so why i create List is because sometime Array dont get fully used to its length
// and get Null values at the end
List<string> dataList = new List<string>();
while (first + last < l+1)
{
dataList.Add((data.Substring(first, last)));
first++;
}
// converts list to Array so array will have values and no Null
// and will use Array.FindAll() later
string[] dataArray = dataList.ToArray(), value;
// get rready a file to start writing on
StreamWriter sw = new StreamWriter(fileName.Substring(0, fileName.Length - 4) + "Results.txt");
//THIS IS THE PART THATS TAKING around 40 minutes
for (int j = 0; j < dataArray.Length; j++)
{
// finds a value in whole array and make array of that finding
value = Array.FindAll(dataArray, str => str.Equals(dataArray[j]));
// value.Length means the count of the Number in the whole array
sw.WriteLine(value.Length);
}
sw.Close();
time.Stop();
Console.WriteLine("Time : " + time.Elapsed);
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Exception " + ex.StackTrace);
Console.ReadLine();
}
}
}
我设置了一个 sequence length = 3
,现在我的程序做了一个数组:
dataArray = {"011" , "110" , "100" , "001" , "010" , "101" , "011"}
通过使用 String.Substring()
。现在我只想计算数组元素的频率。
结果 .txt 文件中的数据
011 - 2
110 - 0
100 - 0
001 - 0
010 - 0
101 - 0
011 - 2
现在看起来很简单其实不然,我无法转换它int
因为它是一个序列我不想丢失序列前面的零
现在我的程序必须循环 1 百万(每个元素)X 1 百万(与数组的每个元素比较)= 1 万亿 次。这需要将近 40 分钟。我想知道我怎样才能让它变得更快,Parallel.For,TPL 我不知道他们如何使用它们。因为它应该在几秒钟内完成。
我的系统规格
32 GB RAM
i7- 5820k 3.30 ghz
64 bit
2x nvidia gtx 970
如果我正确理解你的代码和问题,你需要 "slide a window"(长度为 N,在你的原始代码中为 last
)文本,并计算每个子字符串的次数存在于文中。
如果这是正确的,下面的代码在 million-character 文件上会在 0.292 秒左右完成,而且你根本不需要并行或 GPU。
这里的想法是在我们将 window 滑过文本时将块数统计为 Dictionary
。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
public class Program
{
static Dictionary<string, int> CountChunks(string data, int chunkLength)
{
var chunkCounts = new Dictionary<string, int>();
var l = data.Length;
for (var i = 0; i < l - chunkLength; i++)
{
var chunk = data.Substring(i, chunkLength);
int count = 0;
chunkCounts.TryGetValue(chunk, out count);
chunkCounts[chunk] = count + 1;
}
return chunkCounts;
}
static void Main(string[] args)
{
var time = new Stopwatch();
time.Start();
var fileName = "10.txt";
var data = string.Join("", File.ReadAllText(fileName));
var chunkCounts = CountChunks(data, 15);
using (var sw = new StreamWriter(fileName.Substring(0, fileName.Length - 4) + "Results.txt"))
{
foreach (var pair in chunkCounts)
{
sw.WriteLine($"{pair.Key} - {pair.Value}");
}
}
time.Stop();
Console.WriteLine("Time : " + time.Elapsed);
}
}
输出 10Results.txt
看起来像
011100000111100 - 34
111000001111000 - 37
110000011110001 - 27
100000111100010 - 28
000001111000101 - 37
000011110001010 - 36
000111100010100 - 44
001111000101001 - 35
011110001010011 - 41
111100010100110 - 42
等等
编辑: 这是等效的 Python 程序。稍微慢一点,大约 0.9 秒。
import time
from collections import Counter
t0 = time.time()
c = Counter()
data = ''.join(l for l in open('10.txt'))
l = 15
for i in range(0, len(data) - l):
c[data[i : i + l]] += 1
with open('10Results2.txt', 'w') as outf:
for key, value in c.items():
print(f'{key} - {value}', file=outf)
print(time.time() - t0)
For 循环会给您带来糟糕的性能,因为它必须循环执行一百万个字符串比较。 我建议使用字典而不是列表来将序列存储为键并计为值。 与 while/for 循环相比,它应该会给您带来更好的性能。 您需要做的就是从性能角度稍微调整一下,甚至可能不需要利用 GPU/TLP 运行时,除非这是您的唯一目的。 下面的东西应该让你去。
string keyString = string.Empty;
Dictionary<string,int> dataList = new Dictionary<string,int>;
while (first + last < l+1)
{
keyString = data.Substring(first, last);
if(dataList.ContainsKey(keyString)
{
dataList[keyString] = dataList[keyString] + 1;
}
else
{
dataList.Add(keyString,1);
}
first++;
}
您需要的其余代码是打印这本词典。