Как мне автоматически распараллелить массивы кучи? - PullRequest
0 голосов
/ 14 января 2019

Я пытаюсь написать функцию, которая умножает массив в куче на константу, используя параллельный цикл for, но при попытке выполнить компиляцию в VisualStudio 2017 с / Qpar-report: 2 set, я получаю сообщение " Цикл не распараллелен по причине «1000». Я посмотрел его, и сообщение «Компилятор обнаружил зависимость данных в теле цикла.»:

https://docs.microsoft.com/en-us/cpp/error-messages/tool-errors/vectorizer-and-parallelizer-messages?view=vs-2017#BKMK_ReasonCode100x

В тексте описываются ситуации, когда (например) разные проходы зависят от результатов других проходов, но здесь это неприменимо. Единственная ситуация, о которой я могу подумать, это то, что оптимизатор может беспокоиться о том, что два массива перекрываются в памяти, но как вы убедите компилятор, что это не так?

Я попытался форсировать его с помощью оператора #pragma ivdep, и код компилируется, но функция зависает при вызове.

После долгих проблем я решил, что если я создам фиктивные массивы в стеке внутри функции и зациклю их, он будет успешно распараллеливаться. К сожалению, я не могу полагаться на массивы, которые я принимаю, чтобы они были достаточно маленькими, чтобы полностью копироваться в стек.

Я проверил SO и google на предмет других примеров параллельного выполнения простых операций над массивами, и все они используют выделенные в стеке массивы. Конечно, есть чистый способ распараллелить операцию на массиве кучи ??

#include "stdafx.h"
#include "CppUnitTest.h"
#include "../UnitsConversion/UnitsConversion.h"

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

#define ARRAY_SIZE 10000000

double* testInD;
double* testOutD;

namespace UnitTest
{

TEST_CLASS(Parallel)
{
public:

    TEST_CLASS_INITIALIZE(setup) {
        testInD = new double[ARRAY_SIZE];
        testOutD = new double[ARRAY_SIZE];

        for (int i = 0; i < ARRAY_SIZE; i++) {
            testInD[i] = (double)rand() / (double)RAND_MAX;
            testOutD[i] = (double)rand() / (double)RAND_MAX;
        }

    }

    TEST_CLASS_CLEANUP(cleanup) {
        delete testInD;
        delete testOutD;
    }

    TEST_METHOD(PressuresD)
    {
        Assert::AreEqual(
            (int)1,
            PressureD(
                testInD,
                testOutD,
                ARRAY_SIZE
            )
        );
    }

}

int __stdcall PressureD(
    double* dblInValue,
    double* dblOutValue,
    int n) {

#pragma loop(hint_parallel(0))
    for (int i = 0; i < n; ++i) {
    dblOutValue[i] = dblInValue[i] * 5.0;
    }

    return 1;
}

По какой-то причине мне не повезло найти решение с Google или SO, хотя я думаю, что это будет распространенная проблема. Я что-то упустил?

EDIT:

Изменение цикла на следующее позволяет его распараллелить:

for (int i = 0; i < n; ++i) {
    //dblOutValue[i] = dblInValue[i] * factor; (old version)
    dblOutValue[i] *= factor;
}

Однако, когда я пытаюсь запустить модульное тестирование, код зависает и в конце концов (примерно через 15 секунд) прерывается. Код работает, когда я запускаю его в режиме отладки, но я на 95% уверен, что это потому, что он не паралеллизуется при запуске в отладке.

1 Ответ

0 голосов
/ 15 января 2019

Это простое несоответствие нового / удаления. Ваш модульный тест имеет массив new[] и скаляр delete.

Откажитесь от ручного управления памятью и используйте std::unique_ptr<double[]> или std::vector<double>. Возможно, вам придется захватить пустой указатель на ваши данные перед циклом (на самом деле, PressureD вообще не нужно менять), чтобы распараллеливание было успешным, но вам не нужно вручную управлять временем жизни.

...