C# / C++ 中的同步并行进程
Synchronous Parallel Process in C# / C++
我有一个包含数据的数组 x[]。还有一个"system states"c[]的数组。过程:
for(i = 1; i < N; i++)
{
a = f1(x[i] + c[i-1]);
b = f2(x[i] + c[i-1]);
c[i] = a + b;
}
有没有什么有效的方法可以在使用 2 个并行线程的 2 核系统上找到 f1
和 f2
的值?我的意思是以下(伪代码):
thread_1
{
for(i = 1; i < N; i++)
a = f1(x[i] + c[i-1]);
}
thread_2
{
for(i = 1; i < N; i++)
{
b = f2(x[i] + c[i-1]);
c[i] = a + b; //here we somehow get a{i} from thread_1
}
}
f1
和f2
不耗时,但要计算很多次,所以期望的加速比大约是x2。图形表示见图表:
正在寻找 Windows 的代码示例。
如果我没理解错的话,
a[i]
只有在有c[i-1]
的情况下才能计算
b[i]
只有在有c[i-1]
的情况下才能计算
c[i]
只有在计算a[i]
和b[i]
时才可用
这意味着您唯一可以单独执行的过程是计算a[i]
和b[i]
。
这就是我在 C# 中的看法:
for (int i = 1; i < N; i++)
{
Task<double> calcA = Task.Factory.StartNew(() => { return f1(x[i] + c[i-1]); });
Task<double> calcB = Task.Factory.StartNew(() => { return f2(x[i] + c[i-1]); });
// .Result will block the execution and wait for both calculations to complete
c[i] = calcA.Result + calcB.Result;
}
这将 运行 两个单独的线程,它们将分别计算 f1
和 f2
。 f1
和f2
都计算完后,会设置c[i]
值,下一次迭代运行。
注意:
- 我用
double
,假设你的f1
和f2
returndouble
- 循环从 1 开始,假设您有一些初始值
a[0]
和 b[0]
。否则,c[i-1]
会抛出异常
- 与其他计算
相比,如果 f1
和 f2
的计算确实耗费资源且耗时长,这只会带来改进
Task.Factory.StartNew
(与使用 Thread
不同)使用线程池,这意味着它不会每次都创建一个新线程,而是重用池中的现有线程。它显着减少了开销。
在不进入代码解决方案的情况下,您想使用某种障碍。这允许检查是否所有参与者都已声明他们已完成任务。在此示例中,线程 2 必须等待线程 1
https://en.wikipedia.org/wiki/Barrier_(computer_science)
Example of C++ "Memory barrier"
这个算法中唯一的并行部分是f1和f2的计算,但是你说f1和f2不耗时,所以使用SIMD向量化可能会好得多(例如C#中的System.Numerics.Vectors ) 和 运行 它在一个核心上(这也减少了缓存未命中)。或者您可以修改您的算法以使其可并行化(但这可能需要艰苦的工作)。
我有一个包含数据的数组 x[]。还有一个"system states"c[]的数组。过程:
for(i = 1; i < N; i++)
{
a = f1(x[i] + c[i-1]);
b = f2(x[i] + c[i-1]);
c[i] = a + b;
}
有没有什么有效的方法可以在使用 2 个并行线程的 2 核系统上找到 f1
和 f2
的值?我的意思是以下(伪代码):
thread_1
{
for(i = 1; i < N; i++)
a = f1(x[i] + c[i-1]);
}
thread_2
{
for(i = 1; i < N; i++)
{
b = f2(x[i] + c[i-1]);
c[i] = a + b; //here we somehow get a{i} from thread_1
}
}
f1
和f2
不耗时,但要计算很多次,所以期望的加速比大约是x2。图形表示见图表:
正在寻找 Windows 的代码示例。
如果我没理解错的话,
a[i]
只有在有c[i-1]
的情况下才能计算b[i]
只有在有c[i-1]
的情况下才能计算c[i]
只有在计算a[i]
和b[i]
时才可用
这意味着您唯一可以单独执行的过程是计算a[i]
和b[i]
。
这就是我在 C# 中的看法:
for (int i = 1; i < N; i++)
{
Task<double> calcA = Task.Factory.StartNew(() => { return f1(x[i] + c[i-1]); });
Task<double> calcB = Task.Factory.StartNew(() => { return f2(x[i] + c[i-1]); });
// .Result will block the execution and wait for both calculations to complete
c[i] = calcA.Result + calcB.Result;
}
这将 运行 两个单独的线程,它们将分别计算 f1
和 f2
。 f1
和f2
都计算完后,会设置c[i]
值,下一次迭代运行。
注意:
- 我用
double
,假设你的f1
和f2
returndouble
- 循环从 1 开始,假设您有一些初始值
a[0]
和b[0]
。否则,c[i-1]
会抛出异常 - 与其他计算 相比,如果
Task.Factory.StartNew
(与使用Thread
不同)使用线程池,这意味着它不会每次都创建一个新线程,而是重用池中的现有线程。它显着减少了开销。
f1
和 f2
的计算确实耗费资源且耗时长,这只会带来改进
在不进入代码解决方案的情况下,您想使用某种障碍。这允许检查是否所有参与者都已声明他们已完成任务。在此示例中,线程 2 必须等待线程 1
https://en.wikipedia.org/wiki/Barrier_(computer_science) Example of C++ "Memory barrier"
这个算法中唯一的并行部分是f1和f2的计算,但是你说f1和f2不耗时,所以使用SIMD向量化可能会好得多(例如C#中的System.Numerics.Vectors ) 和 运行 它在一个核心上(这也减少了缓存未命中)。或者您可以修改您的算法以使其可并行化(但这可能需要艰苦的工作)。