【发布时间】:2011-02-09 05:28:41
【问题描述】:
我在玩 C#,想加快程序的速度。我进行了更改并且能够这样做。但是,我需要帮助来理解为什么更改会使其更快。
我已尝试将代码简化为在问题中更易于理解的内容。 Score1 和 Report1 是较慢的方式。 Score2 和 Report2 是更快的方法。第一种方法首先将一个字符串和一个 int 并行存储在一个结构中。接下来,在串行循环中,它遍历这些结构的数组并将它们的数据写入缓冲区。第二种方法首先将数据并行写入字符串缓冲区。接下来,在串行循环中,它将字符串数据写入缓冲区。以下是一些示例运行时间:
运行 1 总平均时间 = 0.492087 秒 运行 2 总平均时间 = 0.273619 秒
当我使用它的早期非并行版本时,时间几乎相同。为什么与并行版本有区别?
即使我减少 Report1 中的循环以将单行输出写入缓冲区,它仍然较慢(总时间约为 0.42 秒)。
这里是简化的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
using System.IO;
namespace OptimizationQuestion
{
class Program
{
struct ValidWord
{
public string word;
public int score;
}
ValidWord[] valid;
StringBuilder output;
int total;
public void Score1(string[] words)
{
valid = new ValidWord[words.Length];
for (int i = 0; i < words.Length; i++)
{
StringBuilder builder = new StringBuilder();
foreach (char c in words[i])
{
if (c != 'U')
builder.Append(c);
}
if (words[i].Length == 3)
{
valid[i] = new ValidWord
{ word = builder.ToString(), score = words[i].Length };
}
}
}
public void Report1(StringBuilder outputBuffer)
{
int total = 0;
foreach (ValidWord wordInfo in valid)
{
if (wordInfo.score > 0)
{
outputBuffer.AppendLine(String.Format("{0} {1}", wordInfo.word.ToString(), wordInfo.score));
total += wordInfo.score;
}
}
outputBuffer.AppendLine(string.Format("Total = {0}", total));
}
public void Score2(string[] words)
{
output = new StringBuilder();
total = 0;
for (int i = 0; i < words.Length; i++)
{
StringBuilder builder = new StringBuilder();
foreach (char c in words[i])
{
if (c != 'U')
builder.Append(c);
}
if (words[i].Length == 3)
{
output.AppendLine(String.Format("{0} {1}", builder.ToString(), words[i].Length));
total += words[i].Length;
}
}
}
public void Report2(StringBuilder outputBuffer)
{
outputBuffer.Append(output.ToString());
outputBuffer.AppendLine(string.Format("Total = {0}", total));
}
static void Main(string[] args)
{
Program[] program = new Program[100];
for (int i = 0; i < program.Length; i++)
program[i] = new Program();
string[] words = File.ReadAllLines("words.txt");
Stopwatch stopwatch = new Stopwatch();
const int TIMING_REPETITIONS = 20;
double averageTime1 = 0.0;
StringBuilder output = new StringBuilder();
for (int i = 0; i < TIMING_REPETITIONS; ++i)
{
stopwatch.Reset();
stopwatch.Start();
output.Clear();
Parallel.ForEach<Program>(program, p =>
{
p.Score1(words);
});
for (int k = 0; k < program.Length; k++)
program[k].Report1(output);
stopwatch.Stop();
averageTime1 += stopwatch.Elapsed.TotalSeconds;
GC.Collect();
}
averageTime1 /= (double)TIMING_REPETITIONS;
Console.WriteLine(string.Format("Run 1 Total Average Time = {0:0.000000} sec", averageTime1));
double averageTime2 = 0.0;
for (int i = 0; i < TIMING_REPETITIONS; ++i)
{
stopwatch.Reset();
stopwatch.Start();
output.Clear();
Parallel.ForEach<Program>(program, p =>
{
p.Score2(words);
});
for (int k = 0; k < program.Length; k++)
program[k].Report2(output);
stopwatch.Stop();
averageTime2 += stopwatch.Elapsed.TotalSeconds;
GC.Collect();
}
averageTime2 /= (double)TIMING_REPETITIONS;
Console.WriteLine(string.Format("Run 2 Total Average Time = {0:0.000000} sec", averageTime2));
Console.ReadLine();
}
}
}
【问题讨论】:
-
你为什么要对 Report1 和 Report2 这样不同的代码进行排名? Report1 包含一个循环,而 Report2 没有。也许在非并行版本中,C# 编译器展开了循环或其他一些魔法?
-
将 Report1 循环减少到一次迭代会有所帮助(0.42 秒),但发布后,我认为这是 Score1 中的数组分配。
-
注意:单词表大约有14000行字符串。所以每次调用 score1 都会分配 14,000 个结构体。
-
我认为你应该尝试做一些分析。如果没有适当的测量,很难准确地说出为什么它会变慢。分配确实很昂贵,但是从您之前的评论来看,我认为结构的 new[] 将转换为: malloc(sizeof(struct) * size);这会很快。结构不作为单独的对象存储在数组中,而是组合在一起。
-
所有这些词的分数不是总是“3”吗?
标签: c# .net multithreading parallel-processing