循环范围拆分中的变量预评估

variables pre-evaluation in loop range spliting

本文关键字:变量 评估 范围 拆分 循环      更新时间:2023-10-16

我想多线程一个for循环,但我的循环中有一些变量需要知道以前的状态。好吧,这并不容易解释。

这是一个示例:

    double mu1 = 0, q1 = 0;
    double max_sigma = 0, max_val = 0;
    for( i = 0; i < N; i++ )
    {
        double p_i, q2, mu2, sigma;
        p_i = h[i]*scale;
        mu1 *= q1;
        q1 += p_i;
        q2 = 1. - q1;
        if(std::min(q1,q2) < FLT_EPSILON || std::max(q1,q2) > 1. -FLT_EPSILON )
            continue;
        mu1 = (mu1 + i*p_i)/q1;
        mu2 = (mu - q1*mu1)/q2;
        sigma = q1*q2*(mu1 - mu2)*(mu1 - mu2);
        if( sigma > max_sigma )
        {
            max_sigma = sigma;
            max_val = i;
        }
    }

scale是一个double标量值。

h是一个std::vector<std::uint64_t>

如果我在 sevral 部分中拆分范围以进行处理任何子范围,我可以在本地(在每个线程中)首先计算p_i

但我不明白我如何确定mu1的价值.

所以我的问题是:有没有办法确定范围 B 的线程开头的mu1,而没有事先mu1范围 A 的线程中处理了什么的结果?如果是,如何?

对于所示的代码,使用多线程解决方案似乎很难实现很多目标。问题在于mu1q1取决于上一个循环的值,因此在上一个循环完成之前,您无法真正继续。

如果你的代码更像是:

for( i = 0; i < N; i++ )
{
    SomeComplexAndSlowCalculation(); // Not depending on mu1 and q1
    mu1 = mu1 * ....;
    q1 = q1 + ....;
    SomeOtherComplexAndSlowCalculation(); // Depending on mu1 and q1
                                          // but not changing them
}

您可以使用std::condition_variable这样的东西:

    SomeComplexAndSlowCalculation(); // Not depending on mu1 and q1
    cv_previous.wait(...);  // wait for thread handling previous index to complete
    mu1 = mu1 * ....;
    q1 = q1 + ....;
    cv_next.notify_one(); // tell thread handling next index to carry on
    SomeOtherComplexAndSlowCalculation(); // Depending on mu1 and q1
                                          // but not changing them

您必须为每个索引启动一个新线程。

为了做出任何改变/改进,这两个功能必须非常慢。

我怀疑并行性会导致速度提高,但您实现这一目标的方法是代数减少计算,使其基于 i 的绝对值而不是先前的状态 (i-1),因此例如替换

p_1 = h[i]*scale;
mu1 *= q1;
q1 += p_1;
mu1 = product_n(pre_scaled_h, 0, i-1);
q1 = sum_n(pre_scaled_h, 0, i);

其中 h[] 是预缩放的,以简化对其的操作,并且定义了 product_n 和 sum_n以计算预缩放 h 中的相应乘积和元素的总和,从 0 到相应的第三个参数(包括 0)(请注意,mu1 基于 i-1 而不是 i,因为在重新计算 q1 之前将其乘以 q1)。

这种代数约简将消除对先前迭代的依赖性,并且应该可以用于除 max_sigma 和 max_val 之外的所有变量,这可能必须在每个单独的线程上计算,然后必须比较相应的线程最大值集才能找到真正的最大值。 对这些线程进行传统的锁定可能会消除任何可能的速度提升,因此处理这些需要自己仔细管理线程(因为例如并发::p arallel_for 不能保证哪个工作块将在给定线程上运行)。

请注意,您应该能够将其简化为单个计算而不是迭代循环(当然,在 h 上进行简单的和/乘积运算),因为代数约简似乎完全基于 h[]。 如果可以将其简化为没有迭代循环的单个方程,则您将获得比任何其他选项更高的性能。