4

100 万行を超えるデータを含む CVS ファイルがあります。効率化するために並行して読む予定です。次のようなことができますか、またはより効率的な方法はありますか?

namespace ParallelData
{
public partial class ParallelData : Form
{
    public ParallelData()
    {
        InitializeComponent();
    }

    private static readonly char[] Separators = { ',', ' ' };

    private static void ProcessFile()
    {
        var lines = File.ReadLines("BigData.csv");
        var numbers = ProcessRawNumbers(lines);

        var rowTotal = new List<double>();
        var totalElements = 0;

        foreach (var values in numbers)
        {
            var sumOfRow = values.Sum();
            rowTotal.Add(sumOfRow);
            totalElements += values.Count;
        }
        MessageBox.Show(totalElements.ToString());
    }

    private static List<List<double>> ProcessRawNumbers(IEnumerable<string> lines)
    {
        var numbers = new List<List<double>>();
        /*System.Threading.Tasks.*/
        Parallel.ForEach(lines, line =>
        {
            lock (numbers)
            {
                numbers.Add(ProcessLine(line));
            }
        });
        return numbers;
    }

    private static List<double> ProcessLine(string line)
    {
        var list = new List<double>();
        foreach (var s in line.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
        {
            double i;
            if (Double.TryParse(s, out i))
            {
                list.Add(i);
            }
        }
        return list;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        ProcessFile();
    }
}
}
4

3 に答える 3

13

それが良い考えかどうかはわかりません。ハードウェアによっては、CPU がボトルネックになることはなく、ディスクの読み取り速度がボトルネックになります。

もう 1 つのポイント: ストレージ ハードウェアが磁気ハードディスクの場合、ディスクの読み取り速度は、ファイルがディスクに物理的にどのように格納されているかに強く関係しています。ファイルが断片化されていない場合 (つまり、すべてのファイル チャンクがディスクにシーケンシャルに格納されている場合)、行ごとにシーケンシャルに読み取ると、パフォーマンスが向上します。

1 つの解決策は、 を使用してファイル全体を一度に読み取り (十分なメモリ容量があれば、100 万行で問題ないはずです) File.ReadAllLines、すべての行を文字列配列に格納してから処理します (つまり、... を使用して解析しstring.Splitます。 ) Parallel.Foreach、行の順序が重要でない場合。

于 2012-08-09T11:11:07.503 に答える
0

私は自分のコンピューターでこれらの行を確認しましたが、Parallel を使用して csv ファイルを読み取ると、CPU を大量に消費する計算は意味をなさないようです。これを並列で実行すると、1 つのスレッドで実行するよりも時間がかかります。ここに私の結果があります: 上記のコードの場合:

2699ms 2712ms (結果確認のため2回チェック)

それから:

private static IEnumerable<List<double>> ProcessRawNumbers2(IEnumerable<string> lines)
{
        var numbers = new List<List<double>>();
        foreach(var line in lines)
        {
            lock (numbers)
            {
                numbers.Add(ProcessLine(line));
            }
        }
    return numbers;
}

私に与えます:2075ms 2106ms

したがって、csvのこれらの数値を何らかの方法で(大規模な計算などで)プログラムで計算してからプログラムに保存する必要がない場合、このような場合に並列処理を使用しても意味がなく、オーバーヘッドが追加されると思います.

于 2012-08-10T06:32:27.933 に答える
0

一般に、複数のスレッドでディスクにアクセスすることは避けるようにしてください。ディスクはボトルネックであり、ブロックされるため、パフォーマンスに影響を与える可能性があります。

ファイル内の行のサイズが問題にならない場合は、最初にファイル全体を読み取ってから、並行して処理する必要があります。

ファイルが大きすぎてそれを実行できない場合、または実用的でない場合は、BlockingCollectionを使用して読み込むことができます。1 つのスレッドを使用してファイルを読み取り、BlockingCollection にデータを入力してから、Parallel.ForEach を使用してその中のアイテムを処理します。BlockingCollection を使用すると、コレクションの最大サイズを指定できるため、コレクションに既に含まれているものが処理されて削除されるため、ファイルからより多くの行を読み取るだけになります。

        static void Main(string[] args)
    {
        string  filename = @"c:\vs\temp\test.txt";
        int maxEntries = 2;

        var c = new BlockingCollection<String>(maxEntries);
        
        var taskAdding = Task.Factory.StartNew(delegate
        {
            var lines = File.ReadLines(filename);
            foreach (var line in lines)
            {
                c.Add(line);    // when there are maxEntries items
                                // in the collection, this line 
                                // and thread will block until 
                                // the processing thread removes 
                                // an item
            }

            c.CompleteAdding(); // this tells the collection there's
                                // nothing more to be added, so the 
                                // enumerator in the other thread can 
                                // end
        });

        while (c.Count < 1)
        {
            // this is here simply to give the adding thread time to
            // spin up in this much simplified sample
        }

        Parallel.ForEach(c.GetConsumingEnumerable(), i =>
           {
               // NOTE: GetConsumingEnumerable() removes items from the 
               //   collection as it enumerates over it, this frees up
               //   the space in the collection for the other thread
               //   to write more lines from the file
               Console.WriteLine(i);  
           });

        Console.ReadLine();
    }

ただし、他のいくつかと同様に、質問をする必要があります。これは、並列化による最適化を試みる必要があるものですか、それともシングルスレッド ソリューションで十分に機能するのでしょうか? マルチスレッドは複雑さを増し、その価値がない場合もあります。

改善したいパフォーマンスはどのようなものですか?

于 2012-08-09T12:35:39.447 に答える