2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘】C#(?)でリストの順番を意識しつつ並列化

Last updated at Posted at 2025-02-15

C#やVBは今月(2025年2月)から触り始めたので、
変なところがあったらすみません。ご指摘等いただけると幸いです。

C#(?)でリストの順番を意識しつつ並列化

現場で.net系を使うことになった際、
「リストを並列処理⇒処理結果を元のリストと同じ順番でリスト化する方法」
を学んだので、備忘として残しておきます。

やりたいこと

リストをfor文などでループし別リストを作成する処理を並列化(パフォーマンス改善)

やったこと

・修正前コード例

using System.ComponentModel;

var listA = new List<int>();

// 1万件のリストを用意(実際は10万件くらい)
for (int i = 0; i < 10000; i++)
{
    listA.Add(i);
}

var startDate = DateTime.Now;

var listB = new List<int>();
for (int i = 0; i < listA.Count; i++)
{
    listB.Add(listA[i]);
}

var endDate = DateTime.Now;
Console.WriteLine("処理時間:" + (endDate - startDate).TotalMilliseconds + "ms");

・修正後コード例

(略)
// ポイント1:listAと同じだけのsizeを持つ、格納先listを作成
var listB = new List<int>(new int[listA.Count]);

var startDate = DateTime.Now;
// ポイント2:並列化し、indexに対応するところにデータを入れる
Parallel.ForEach(listA, (data, state, index) =>
{
    listB[Convert.ToInt32(index)] = listA[Convert.ToInt32(index)];
});
var endDate = DateTime.Now;
Console.WriteLine("処理時間:" + (endDate - startDate).TotalMilliseconds + "ms");

以下コメントで頂いたもの

・1行で記載する方法

(略)
var startDate = DateTime.Now;
//お手軽な書き方(実質一行)
var listB = listA.AsParallel()
        .AsOrdered()  //順序を維持するよう指示
                      //.Select(data => data * 2)  //何か処理をした結果を渡したい場合はここに処理を書く
        .ToList();
var endDate = DateTime.Now;
Console.WriteLine("処理時間:" + (endDate - startDate).TotalMilliseconds + "ms");

・Zipで記載する方法

(略)
List<int> listB = [.. new int[listA.Count]];

var startDate = DateTime.Now;
listA.Zip(listA.Select((_, i) => i)).AsParallel().ForAll(data => listB[data.Second] = data.First);

var endDate = DateTime.Now;
Console.WriteLine("処理時間:" + (endDate - startDate).TotalMilliseconds + "ms");

・AsOrderedで記載する方法

(略)
var startDate = DateTime.Now;
List<int> listB = [];

// AsOrdered()を使い、順序を保持
listA.AsParallel().AsOrdered().ForAll(listB.Add);

var endDate = DateTime.Now;
Console.WriteLine("処理時間:" + (endDate - startDate).TotalMilliseconds + "ms");

それぞれ実際に動かしてみた

<修正前>
 1回目:5.1387ms
 2回目:6.7196ms
 3回目:4.7355ms

<修正後>
 1回目:14.5718ms
 2回目:14.2512ms
 3回目:15.6844ms

<1行>
 1回目:16.1567ms
 2回目:15.0027ms
 3回目:14.1936ms

<Zip>
1回目:20.1356ms
2回目:18.8725ms
3回目:20.6022ms

<AsOrdered>
 1回目:12.4384ms
 2回目:13.1296ms
 3回目:13.9489ms

【補足】
ちなみに、修正後の方が遅くなったので、
試しに修正前後のループ箇所に "Thred.sleep(5)" を入れて実行してみた。
修正前:154720.268ms
修正後:9000.7618ms

まとめ

並列化すれば絶対に早くなるわけでもないらしい。
並列化したら早くなるかは、実際に試して確認すべきみたいです。
(現場は1/3~1/4位の処理時間にはなった)

2
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?