使用异步C ++并行向量乘法

时间:2016-05-05 19:59:27

标签: c++ vector parallel-processing future stdasync

我正在尝试并行化一段代码,该代码将复数浮点数的两个向量相乘并对结果求和。要做到这一点,我试图使用std :: async与期货。我的想法是将矢量分成8个部分并对这8个部分中的每个部分进行并行处理,然后将它们相加作为我的最终结果。为此,我创建8个期货,每个期货包含一个lambda,它将两个向量相乘并对结果求和。每个未来都会传递指向矢量不同位置的指针,这些位置代表了这个特定未来应该采取行动的矢量部分。

然而它似乎没有给我我预期的速度提升,它可能会加速这部分代码增加20-30%,但就是这样,此外负载似乎并没有扩散到我的核心(4或8有超线程),但似乎都是在100%的核心上。

我已经包含了以下代码。任何建议都将不胜感激。

size_t size = Input1.size()/8;

std::vector<std::future<complex<float> > > futures;
futures.reserve(8);

for(int i = 0; i<8; ++i)
{
    futures.push_back(std::async( [](complex<float>* pos, complex<float>*pos2, size_t siz)
    {
        complex<float> resum(0,0);
        for(int i = 0; i < siz; ++i)
            resum += pos[i]*pos2[i];
        return resum;
    }, &Input1[i*size], &Input2[i*size], size));
}


complex<float> ResSum(0,0);
for(int i = 0; i < futures.size(); ++i)
    ResSum += futures.at(i).get();

2 个答案:

答案 0 :(得分:1)

如上所述,对std::async的调用获得了launch::any的默认启动策略,该策略允许在单个线程上运行所有异步。要坚持使用单独的线程,请将launch::async作为std::async调用中的第一个参数传递。

答案 1 :(得分:1)

这取决于你扔多少数据。

在以下示例中,通过简单的循环,4096个条目将更快。但是,对于1000 * 4096条目,并行版本更快。

因此,你所提出的20-30%的改善结果可能恰好落在该范围之间。

这是我使用的测试程序。

第一次运行是简单循环,第二次运行来自问题,第三次使用std::launch::async

Plain       From        With
loop        question    launch::async
First       Second      Third
166         1067        607     
166         614         434     
166         523         509     
265993      94633       66231       
182981      60594       69537       
237767      65731       57256   

这是live result

#include <vector>
#include <thread>
#include <future>
#include <complex>
#include <string>
#include <iostream>
#include <chrono>
#include <random>
#include <ratio>

float get_random()
{
    static std::default_random_engine e;
    static std::uniform_real_distribution<> dis(0,1); // rage 0 - 1
    return static_cast<float>(dis(e));
}

void do_tests(float val1, float val2, float val3, float val4, int multiplier)
{
    {
        std::vector<std::complex<float>> Input1(4096*multiplier,std::complex<float>{val1,val2});
        std::vector<std::complex<float>> Input2(4096*multiplier,std::complex<float>{val3,val4});
        std::complex<float> ResSum(0,0);
        auto start{std::chrono::high_resolution_clock::now()};

        size_t size = Input1.size();
        for (int i=0; i<size; ++i) {
            ResSum += Input1[i]*Input2[i];
        }

        auto end{std::chrono::high_resolution_clock::now()};
        auto time_used{end-start};
        std::cout << std::chrono::duration_cast<std::chrono::microseconds>(time_used).count() << "\t\t";
    }

    {
        std::vector<std::complex<float>> Input1(4096*multiplier,std::complex<float>{val1,val2});
        std::vector<std::complex<float>> Input2(4096*multiplier,std::complex<float>{val3,val4});
        std::complex<float> ResSum(0,0);
        auto start{std::chrono::high_resolution_clock::now()};

        size_t size = Input1.size()/8;
        std::vector<std::future<std::complex<float>>> futures;
        futures.reserve(8);

        for (int i = 0; i<8; ++i) {
            futures.push_back(
                std::async(
                    [](std::complex<float>* pos,std::complex<float>*pos2,size_t siz) {
                std::complex<float> resum(0,0);
                for (int i = 0; i < siz; ++i)
                    resum += pos[i]*pos2[i];
                return resum;
            }
                    ,&Input1[i*size],&Input2[i*size],size
                )
                );
        }

        for (int i = 0; i < futures.size(); ++i)
            ResSum += futures.at(i).get();

        auto end{std::chrono::high_resolution_clock::now()};
        auto time_used{end-start};
        std::cout << std::chrono::duration_cast<std::chrono::microseconds>(time_used).count() << "\t\t";
    }


    {
        std::vector<std::complex<float>> Input1(4096*multiplier,std::complex<float>{val1,val2});
        std::vector<std::complex<float>> Input2(4096*multiplier,std::complex<float>{val3,val4});
        std::complex<float> ResSum(0,0);
        auto start{std::chrono::high_resolution_clock::now()};

        size_t size = Input1.size()/8;
        std::vector<std::future<std::complex<float>>> futures;
        futures.reserve(8);

        for (int i = 0; i<8; ++i) {
            futures.push_back(
                std::async(std::launch::async,
                    [](std::complex<float>* pos,std::complex<float>*pos2,size_t siz) {
                std::complex<float> resum(0,0);
                for (int i = 0; i < siz; ++i)
                    resum += pos[i]*pos2[i];
                return resum;
            }
                    ,&Input1[i*size],&Input2[i*size],size
                )
                );
        }

        for (int i = 0; i < futures.size(); ++i)
            ResSum += futures.at(i).get();

        auto end{std::chrono::high_resolution_clock::now()};
        auto time_used{end-start};
        std::cout << std::chrono::duration_cast<std::chrono::microseconds>(time_used).count() << "\t\t";
    }

    std::cout << '\n';

}

int main()
{
    float val1{get_random()};
    float val2{get_random()};
    float val3{get_random()};
    float val4{get_random()};

    std::cout << "First\t\tSecond\t\tThird\n";

    do_tests(val1, val2, val3, val4, 1);
    do_tests(val1, val2, val3, val4, 1);
    do_tests(val1, val2, val3, val4, 1);
    do_tests(val1, val2, val3, val4, 1000);
    do_tests(val1, val2, val3, val4, 1000);
    do_tests(val1, val2, val3, val4, 1000);


}