前回は
シングルスレッドだけでの計算だった
今回はマルチスレッド
1から100万1までの連番の合計を1000回求めるのにかかった時間
合計値の正解は500001500001
計測アプリ
ボタンを押して待つだけ、test2とtest11だけは極端に遅い、test5とtest21はオーバーフローで間違った値が表示される
アプリダウンロード
20200207_int配列の値の合計マルチスレッド.zip
ギットハブ
github.com
環境
CPU AMD Ryzen 5 2400G(4コア8スレッド)
MEM DDR4-2666
Window 10 Home 64bit
Visual Studio 2019 Community .NET Core 3.1 WPF C#
結果をグラフで
一番上の赤のグラフが普通のForでループして足し算したものはシングルスレッド、これを基準にして、それ以外はマルチスレッドでいろいろ。
基準の速度を1としたときの相対速度、最大で13倍も速くなっているのがあるけど、これはめんどくさい条件下での値。それ以外だと3倍くらい速くなった
基準にするシングルスレッドのFor、普通
単純にParallelForにするだけだと同時に足し算になったときに値がおかしくなるので、排他処理してくれるInterlockedのAddを使って足し算。
これはものすごく遅くなって、基準の30倍以上の処理時間になった。Interlocked.Addってのがとても重い処理みたいなんだけど必要
Interlocked
今回だとtotalがこの、複数のスレッドで共有される変数、なんだと思う。それに対して複数スレッドから同時にアクセスできないように排他処理をしてくれるみたい
Interlocked.Add
排他処理での足し算専用のメソッド、素晴らしい。今回のテストではほとんどのメソッドで使っている。
LINQはAsParallelをつけると自動でマルチスレッドで処理してくれるので楽ちん。基準の3割程度の速度しかないけどAsParallelをつけないSumより3倍速くなっている!
ParallelForEachとパーティションローカル変数
docs.microsoft.com
Parallel.ForEachを使った方法
docs.microsoft.com
これの
Parallel.ForEach<TSource,TLocal>(
IEnumerable<TSource>,
Func<TLocal>,
Func<TSource,ParallelLoopState,TLocal,TLocal>,
Action<TLocal>)
これが
private long Test30_ParallelForEach(int[] ary)
{
long total = 0;
Parallel.ForEach<int, long>(
ary,
() => 0,
(item, state, subtotal) => { return subtotal += item; },
(x) => Interlocked.Add(ref total, x));
return total;
}
こう
ParallelForEachは処理する配列をパーティションで区切って、それぞれの配列をスレッドに割り当てる感じ?スレッドごとに変数を持つことができて、それがパーティションローカル変数で、それはスレッドごとなので干渉することなく処理できる、つまり排他処理できるってことかなあ。
今回だとパーティションローカル変数にあたるのはsubtotal?
第1引数に処理する配列を渡すのはわかる
第2引数は"各タスクのローカルデータの初期状態を返す関数デリゲート"これは変数に初期値を与えるみたいで今回は足し算なので0
第3引数は"2回の反復処理につき1回呼び出されるデリゲート"ここが実際のループの処理みたい、引数は3つあって順番に各要素、ループのステータス、処理の結果用?今回はループのステータスは使っていない、結果用の変数に各要素を足し算しているだけ
第4引数は"各タスクのローカル状態に対して最後の操作を行うデリゲート"ここで集計している。スレッドごとのローカル変数が返ってくるから、それを集計している感じ
よくわからんけど結果は基準の半分の速度
ParallelForとスレッドローカル変数
さっきのが
ParallelForEachのパーティションローカル変数
で、これは
ParallelForのスレッドローカル変数
似ている、速度も同じくらいだけど少し速くて基準の0.6倍速
Taskでマルチ
CPUのスレッド数(8スレッド)で配列を分割してTaskでマルチ、分割にはLINQのSkipとTakeを使用
結果は基準の0.4倍速でイマイチ
Partitionerクラスを使ってマルチ
docs.microsoft.com
こちらを参考にして書いてみたのが
結果は今回のテストでは一番遅くて基準の30倍以上の処理時間になったけど、これは書き方というかPartitionerの使い方が良くなかったみたい
Partitionerクラス
Createで作成
これをParallelForEachの第1引数に渡して
第2引数はまたデリゲートで、その第1引数は区切られた配列のインデックスの最初と最後が入るみたいで、一時停止してみてみると
これかなあ
こんななってた、ここではこのループの中で直接Interlockedを使って足し算したのが良くなかった、これだと区切ったのが意味なくなっている
正しい使い方はこうだった
www.it-swarm.dev
2019/06/15 Theodor Zoulias=サンの投稿をそのままコピペ
基準の3倍以上の速度!やっとシングルより速くなった!
さっきのPartitionerを使ったのと違うのは、Forの外側にローカル変数としてsubtotalを置いただけ。これはParallelForとスレッドローカル変数を使った方法に似ているけど、処理速度はぜんぜん違うねえ
これに気を良くしてパーティションの区切り方やParallelのオプションで最大スレッド数とかを変えてみたけど大きな変化はなかったのが
このへん、Test70から73は誤差程度の差
区切り指定あり+スレッド数指定ありが気持ち遅くて、それ以外は誤差
System.Numerics.Vector.Add(SIMD)で足し算をTaskでマルチスレッド
遅くなった…ParallelForやParallelForEachでローカル変数を使った方法よりも遅い
普通のForをTaskでマルチにしたときよりも僅かに遅いとか、かなり残念な結果
Partitioner+VectorAdd
基準よりは速くなったけどねえ、やっぱりintからlongに変換するところがネックになる。
じゃあ変換しなくてもいいようにlong型配列ならどうなるかなと
速くはなったけど2倍にとどまる
じゃあint型配列をint型で集計なら
一気に13倍まで速くなったw
前回のシングル同士では5~6倍だったのでマルチスレッド化で、さらに2倍速くなったことになる。合計値がオーバフローしないことがわかっているならこれがいい
int型配列の合計値
普通のForループ
速度:★★★
書く手間:★★★
普通だけどこれが速い
LINQのSum
速度:★(1/10倍~1/3倍)
書く手間:★
Sumだけなら1/10、どうせならAsParallelをつけてマルチスレッドにして1/3倍速
ParallelForEach+Partitioner+パーティションローカル変数
速度:★★★★★★★★★★(3倍以上)
書く手間:★★★★★
Partitionerクラスを使って区切った配列をParallelForEachに渡して、スレッドごとの小計はパーティションローカル変数(スレッドローカル変数)を使って、計算自体は普通のForを使う、合計は排他処理のInterlockedAddで行う
手間はかかるけど見合った結果だと思うし、コア数の多いCPUならもっと速くなるはず
VectorAdd+ParallelForEach+Partitioner+パーティションローカル変数
速度:★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★(条件付き13倍)
書く手間:★★★★★★★
合計値が配列の型でもオーバフローしないことがわかっているなら、VectorAddを使って計算、それ以外はさっきの3倍速の方法と同じ
2020/02/11追記ここから
VectorWiden+VectorAdd+ParallelForEach+Partitioner+パーティションローカル変数
速度:★★★★★★★★★★★★★★★★★★★(6倍以上)
書く手間:★★★★
多分これが一番早いと思います
gogowaten.hatenablog.com
2020/02/11追記ここまで
日記
ここ1周間は小学1年生かよってくらい足し算ばかりしていた、いろいろな方法があるねえ
使っているCPUが2年前のミドルローレンジのRyzen 5 2400Gだから、Zen2コアの第3世代RyzenやIntelの新し目のCPUだと同じコア数でもVectorAddを使ったところはもっと速いかも?マルチスレッドならコア数が多いほうが速いはずだけどどれくらい速くなるんだろう、たとえばこれとか
pc.watch.impress.co.jp
Windowsやベンチマークアプリでも64コア128スレッドには対応が追いついていないのんね、64コアだもんなあ、2400Gの16個分!ってことはレモンだと…?
コード全部
XAML
<Window xClass="_20200207_int配列の値の合計マルチスレッド.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlnsx="http://schemas.microsoft.com/winfx/2006/xaml"
xmlnsd="http://schemas.microsoft.com/expression/blend/2008"
xmlnsmc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlnslocal="clr-namespace:_20200207_int配列の値の合計マルチスレッド"
mcIgnorable="d"
Title="MainWindow" Height="600" Width="614">
<Grid>
<StackPanel>
<StackPanelResources>
<Style TargetType="StackPanel">
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="Button">
<Setter Property="Width" Value="60"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="2,0"/>
</Style>
</StackPanelResources>
<TextBlock xName="MyTextBlock" Text="text" HorizontalAlignment="Center"/>
<TextBlock xName="MyTextBlockVectorCount" Text="vectorCount" HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal">
<Button xName="Button1" Content="test1"/>
<TextBlock xName="Tb1" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button2" Content="test2"/>
<TextBlock xName="Tb2" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button3" Content="test3"/>
<TextBlock xName="Tb3" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button4" Content="test4"/>
<TextBlock xName="Tb4" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button5" Content="test5"/>
<TextBlock xName="Tb5" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button6" Content="test6"/>
<TextBlock xName="Tb6" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button7" Content="test7"/>
<TextBlock xName="Tb7" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button8" Content="test8"/>
<TextBlock xName="Tb8" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button9" Content="test9"/>
<TextBlock xName="Tb9" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button10" Content="test10"/>
<TextBlock xName="Tb10" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button11" Content="test11"/>
<TextBlock xName="Tb11" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button12" Content="test12"/>
<TextBlock xName="Tb12" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button13" Content="test13"/>
<TextBlock xName="Tb13" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button14" Content="test14"/>
<TextBlock xName="Tb14" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button15" Content="test15"/>
<TextBlock xName="Tb15" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button16" Content="test16"/>
<TextBlock xName="Tb16" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button17" Content="test17"/>
<TextBlock xName="Tb17" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button18" Content="test18"/>
<TextBlock xName="Tb18" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button19" Content="test19"/>
<TextBlock xName="Tb19" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button20" Content="test20"/>
<TextBlock xName="Tb20" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button21" Content="test21"/>
<TextBlock xName="Tb21" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button22" Content="test22"/>
<TextBlock xName="Tb22" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button23" Content="test23"/>
<TextBlock xName="Tb23" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button24" Content="test24"/>
<TextBlock xName="Tb24" Text="time"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button xName="Button25" Content="test25"/>
<TextBlock xName="Tb25" Text="time"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
XAML.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Numerics;
using System.Threading;
namespace _20200207_int配列の値の合計マルチスレッド
{
<summary>
</summary>
public partial class MainWindow : Window
{
private int[] MyIntAry;
private long[] MyLongAry;
private const int LOOP_COUNT = 1000;
private const int ELEMENT_COUNT = 1_000_001;
public MainWindow()
{
InitializeComponent();
MyTextBlock.Text = $"配列の値の合計、要素数{ELEMENT_COUNT.ToString("N0")}の合計を{LOOP_COUNT}回求める処理時間";
MyTextBlockVectorCount.Text = $"Vector<long>.Count = {Vector<long>.Count}";
MyIntAry = Enumerable.Range(1, ELEMENT_COUNT).ToArray();
MyLongAry = new long[MyIntAry.Length];
MyIntAry.CopyTo(MyLongAry, 0);
Button1.Click += (s, e) => MyExe(Test0_For, Tb1);
Button2.Click += (s, e) => MyExe(Test10_ParallelFor, Tb2);
Button3.Click += (s, e) => MyExe(Test20_AsParallelSum, Tb3);
Button4.Click += (s, e) => MyExe(Test30_ParallelForEach, Tb4);
Button5.Click += (s, e) => MyExe(Test40_ParallelForThreadLocalVariable, Tb5);
Button6.Click += (s, e) => MyExe(Test4x_ParallelForThreadLocalVariable_Overflow, Tb6);
Button7.Click += (s, e) => MyExe(Test41_ParallelForThreadLocalVariable, Tb7);
Button8.Click += (s, e) => MyExe(Test42_ParallelForThreadLocalVariable, Tb8);
Button9.Click += (s, e) => MyExe(Test43_ParallelForThreadLocalVariable, Tb9);
Button10.Click += (s, e) => MyExe(Test50_TaskLinqSkipTake, Tb10);
Button11.Click += (s, e) => MyExe(Test60_ParallelForEachPartitioner, Tb11);
Button12.Click += (s, e) => MyExe(Test70_ParallelForEachPartitionerThreadLocalVariable, Tb12);
Button13.Click += (s, e) => MyExe(Test71_ParallelForEachPartitionerThreadLocalVariable, Tb13);
Button14.Click += (s, e) => MyExe(Test72_ParallelForEachPartitionerThreadLocalVariable, Tb14);
Button15.Click += (s, e) => MyExe(Test73_ParallelForEachPartitionerThreadLocalVariable, Tb15);
Button16.Click += (s, e) => MyExe(Test80_TaskLinqSkipTake_Vector, Tb16);
Button17.Click += (s, e) => MyExe(Test81_TaskVectorAdd_ForInt, Tb17);
Button18.Click += (s, e) => MyExe(Test82_TaskVectorAdd_ForLong, Tb18);
Button19.Click += (s, e) => MyExe(Test90_ParallelForEachPartitioner_Vector, Tb19);
Button20.Click += (s, e) => MyExe(Test91_ParallelForEachPartitioner_Vector, Tb20);
Button21.Click += (s, e) => MyExe(Test9x_ParallelForEachPartitionerVector_Overflow, Tb21);
}
private long Test0_For(int[] Ary)
{
long total = 0;
for (int i = 0; i < Ary.Length; i++)
{
total += Ary[i];
}
return total;
}
private long Test10_ParallelFor(int[] Ary)
{
long total = 0;
Parallel.For(0, Ary.Length, n =>
{
Interlocked.Add(ref total, Ary[n]);
});
return total;
}
private long Test20_AsParallelSum(int[] ary)
{
return ary.AsParallel().Sum(i => (long)i);
}
private long Test30_ParallelForEach(int[] ary)
{
long total = 0;
Parallel.ForEach<int, long>(
ary,
() => 0,
(item, state, subtotal) => { return subtotal += item; },
(x) => Interlocked.Add(ref total, x));
return total;
}
private long Test31_ParallelForEach(int[] ary)
{
long total = 0;
Parallel.ForEach(ary,
() => 0,
(item, state, subtotal) => { return subtotal += item; },
(long x) => Interlocked.Add(ref total, x));
return total;
}
private long Test40_ParallelForThreadLocalVariable(int[] ary)
{
long total = 0;
Parallel.For<long>(0, ary.Length,
() => 0,
(j, loop, subtotal) => { return subtotal += ary[j]; },
(x) => Interlocked.Add(ref total, x));
return total;
}
private long Test4x_ParallelForThreadLocalVariable_Overflow(int[] ary)
{
long total = 0;
Parallel.For(0, ary.Length,
() => 0,
(j, loop, subtotal) => { return subtotal += ary[j]; },
(x) => Interlocked.Add(ref total, x));
return total;
}
private long Test41_ParallelForThreadLocalVariable(int[] ary)
{
long total = 0;
Parallel.For(0, ary.Length,
() => 0,
(int j, ParallelLoopState loop, long subtotal) => { return subtotal += ary[j]; },
(x) => Interlocked.Add(ref total, x));
return total;
}
private long Test42_ParallelForThreadLocalVariable(int[] ary)
{
long total = 0;
Parallel.For(0, ary.Length,
() => 0,
(j, loop, subtotal) => { return subtotal += ary[j]; },
(long x) => Interlocked.Add(ref total, x));
return total;
}
private long Test43_ParallelForThreadLocalVariable(int[] ary)
{
long total = 0;
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
Parallel.For<long>(0, ary.Length,
options,
() => 0,
(j, loop, subtotal) => { return subtotal += ary[j]; },
(x) => Interlocked.Add(ref total, x));
return total;
}
private long Test50_TaskLinqSkipTake(int[] ary)
{
long total = 0;
int cpuThread = Environment.ProcessorCount;
int windowSize = ary.Length / cpuThread;
long[] neko = Task.WhenAll(Enumerable.Range(0, cpuThread).Select(x =>
Task.Run(() =>
{
var ii = ary.Skip(windowSize * x).Take(windowSize).ToArray();
return Test0_For(ii);
}))).GetAwaiter().GetResult();
total = neko.Sum();
int lastIndex = ary.Length - (ary.Length % windowSize);
for (int i = lastIndex; i < ary.Length; i++)
{
total += ary[i];
}
return total;
}
private long Test60_ParallelForEachPartitioner(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
Parallel.ForEach(rangePartitioner, (range, loopState) =>
{
for (int i = range.Item1; i < range.Item2; i++)
{
Interlocked.Add(ref total, ary[i]);
}
});
return total;
}
private long Test70_ParallelForEachPartitionerThreadLocalVariable(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
Parallel.ForEach(rangePartitioner, (range) =>
{
long subtotal = 0;
for (int i = range.Item1; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private long Test71_ParallelForEachPartitionerThreadLocalVariable(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
var options = new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount };
Parallel.ForEach(rangePartitioner, options, (range) =>
{
long subtotal = 0;
for (int i = range.Item1; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private long Test72_ParallelForEachPartitionerThreadLocalVariable(int[] ary)
{
long total = 0;
var rangePartitioner = Partitioner.Create(0, ary.Length);
Parallel.ForEach(rangePartitioner, (range) =>
{
long subtotal = 0;
for (int i = range.Item1; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private long Test73_ParallelForEachPartitionerThreadLocalVariable(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length);
var options = new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount };
Parallel.ForEach(rangePartitioner, options, (range) =>
{
long subtotal = 0;
for (int i = range.Item1; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private long TestVectorAddEach(int[] Ary)
{
int simdLength = Vector<long>.Count;
int lastIndex = Ary.Length - (Ary.Length % simdLength);
var longAry = new long[simdLength];
var v = new Vector<long>(longAry);
for (int j = 0; j < lastIndex; j += simdLength)
{
for (int i = 0; i < simdLength; i++)
{
longAry[i] = Ary[j + i];
}
v = System.Numerics.Vector.Add(v, new Vector<long>(longAry));
}
long total = 0;
for (int i = 0; i < simdLength; i++)
{
total += v[i];
}
for (int i = lastIndex; i < Ary.Length; i++)
{
total += Ary[i];
}
return total;
}
private long Test80_TaskLinqSkipTake_Vector(int[] ary)
{
long total = 0;
int cpuThread = Environment.ProcessorCount;
int windowSize = ary.Length / cpuThread;
long[] neko = Task.WhenAll(Enumerable.Range(0, cpuThread).Select(x =>
Task.Run(() =>
{
var ii = ary.Skip(windowSize * x).Take(windowSize).ToArray();
return TestVectorAddEach(ii);
}))).GetAwaiter().GetResult();
total = neko.Sum();
int lastIndex = ary.Length - (ary.Length % windowSize);
for (int i = lastIndex; i < ary.Length; i++)
{
total += ary[i];
}
return total;
}
private long Test81_TaskVectorAdd_ForInt(int[] ary)
{
long total = 0;
int cpuThread = Environment.ProcessorCount;
int windowSize = ary.Length / cpuThread;
long[] neko = Task.WhenAll(Enumerable.Range(0, cpuThread).Select(x =>
Task.Run(() =>
{
var ii = new int[windowSize];
for (int i = 0; i < windowSize; i++)
{
ii[i] = ary[i + (x * windowSize)];
}
return TestVectorAddEach(ii);
}))).GetAwaiter().GetResult();
total = neko.Sum();
int lastIndex = ary.Length - (ary.Length % windowSize);
for (int i = lastIndex; i < ary.Length; i++)
{
total += ary[i];
}
return total;
}
private long Test82_TaskVectorAdd_ForLong(int[] ary)
{
long total = 0;
int cpuThread = Environment.ProcessorCount;
int windowSize = ary.Length / cpuThread;
long[] neko = Task.WhenAll(Enumerable.Range(0, cpuThread).Select(x =>
Task.Run(() =>
{
var ll = new long[windowSize];
var p = x * windowSize;
for (int i = 0; i < windowSize; i++)
{
ll[i] = ary[i + p];
}
return TestLongVectorAdd(ll);
}))).GetAwaiter().GetResult();
total = neko.Sum();
int lastIndex = ary.Length - (ary.Length % windowSize);
for (int i = lastIndex; i < ary.Length; i++)
{
total += ary[i];
}
return total;
}
private long TestLongVectorAdd(long[] ary)
{
int simdLength = Vector<long>.Count;
int lastIndex = ary.Length - (ary.Length % simdLength);
var v = new Vector<long>();
for (int i = 0; i < lastIndex; i += simdLength)
{
v = System.Numerics.Vector.Add(v, new Vector<long>(ary, i));
}
long total = 0;
for (int i = 0; i < simdLength; i++)
{
total += v[i];
}
for (int i = lastIndex; i < ary.Length; i++)
{
total += ary[i];
}
return total;
}
private long Test90_ParallelForEachPartitioner_Vector(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
int simdLength = Vector<long>.Count;
Parallel.ForEach(rangePartitioner, (range) =>
{
var v = new Vector<long>();
var l = new long[simdLength];
int lastIndex = range.Item2 - (range.Item2 % simdLength);
for (int i = range.Item1; i < lastIndex; i += simdLength)
{
for (int j = 0; j < simdLength; j++)
{
l[j] = ary[i + j];
}
v = System.Numerics.Vector.Add(v, new Vector<long>(l));
}
long subtotal = 0;
for (int i = 0; i < simdLength; i++)
{
subtotal += v[i];
}
for (int i = lastIndex; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private long Test91_ParallelForEachPartitioner_Vector(long[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
int simdLength = Vector<long>.Count;
Parallel.ForEach(rangePartitioner, (range) =>
{
var v = new Vector<long>();
int lastIndex = range.Item2 - (range.Item2 % simdLength);
for (int i = range.Item1; i < lastIndex; i += simdLength)
{
v = System.Numerics.Vector.Add(v, new Vector<long>(ary, i));
}
long subtotal = 0;
for (int i = 0; i < simdLength; i++)
{
subtotal += v[i];
}
for (int i = lastIndex; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private long Test9x_ParallelForEachPartitionerVector_Overflow(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
int simdLength = Vector<int>.Count;
Parallel.ForEach(rangePartitioner, (range) =>
{
int lastIndex = range.Item2 - (range.Item2 % simdLength);
var v = new Vector<int>();
for (int i = range.Item1; i < lastIndex; i += simdLength)
{
v = System.Numerics.Vector.Add(v, new Vector<int>(ary, i));
}
long subtotal = 0;
for (int i = 0; i < simdLength; i++)
{
subtotal += v[i];
}
for (int i = lastIndex; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
private void MyExe(Func<int[], long> func, TextBlock tb)
{
long total = 0;
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < LOOP_COUNT; i++)
{
total = func(MyIntAry);
}
sw.Stop();
tb.Text = $"処理時間:{sw.Elapsed.TotalSeconds.ToString("00.000")}秒 合計値:{total} {System.Reflection.RuntimeReflectionExtensions.GetMethodInfo(func).Name}";
}
private void MyExe(Func<long[], long> func, TextBlock tb)
{
long total = 0;
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < LOOP_COUNT; i++)
{
total = func(MyLongAry);
}
sw.Stop();
tb.Text = $"処理時間:{sw.Elapsed.TotalSeconds.ToString("00.000")}秒 合計値:{total} {System.Reflection.RuntimeReflectionExtensions.GetMethodInfo(func).Name}";
}
}
}
関連記事
追記にも書いたけど、次回は3日後
gogowaten.hatenablog.com
前回は一昨日
前々回は2週間前
gogowaten.hatenablog.com