Добавление double в параллельном цикле - std :: atomic <double> - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть параллельный код, который выполняет некоторые вычисления, а затем добавляет значение double к двойной переменной вне цикла.Я попытался использовать std :: atomic, но он не имеет поддержки для арифметических операций над переменными std :: atomic .

double dResCross = 0.0;
std::atomic<double> dResCrossAT = 0.0;

Concurrency::parallel_for(0, iExperimentalVectorLength, [&](size_t m)
{
     double value;
     //some computation of the double value
     atomic_fetch_add(&dResCrossAT, value);
});
dResCross += dResCrossAT;

Простое написание

dResCross += value;

, очевидно, не имеет смысла.У меня вопрос, как я могу решить эту проблему, не делая серийный код?

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Типичный способ атомарного выполнения арифметических операций с типом с плавающей запятой - это цикл сравнения и обмена (CAS).

 double value;
 //some computation of the double value

 double expected = atomic_load(&dResCrossAT);

 while (!atomic_compare_exchange_weak(&dResCrossAT, &expected, expected + value));

Подробное объяснение можно найти в статье Джеффа Прешинга об этом классе операций.

0 голосов
/ 03 декабря 2018

Я считаю, что исключение частичной записи в память в неатомарную переменную требует мьютексирования, я не уверен, что это единственный способ убедиться, что нет конфликта записи, но это достигается следующим образом

#include <mutex>
#include <thread>

std::mutex mtx;

void threadFunction(double* d){
    while (*d < 100) {
        mtx.lock();
        *d += 1.0;
        mtx.unlock();
    }
}

int main() {
    double* d = new double(0);
    std::thread thread(threadFunction, d);
    while (true) {
        if (*d == 100) {
            break;
        }
    }
    thread.join();
}

Который добавит 1.0 к d 100 раз потокобезопасным способом.Блокировка и разблокировка мьютекса гарантируют, что только один поток обращается к d в данный момент времени.Тем не менее, это значительно медленнее, чем эквивалент atomic, потому что блокировка и разблокировка стоят очень дорого - я слышал разные вещи в зависимости от операционной системы и конкретного процессора и того, что блокируется или разблокируется, но это примерно 50 тактовых циклов дляэтот пример, но это может потребовать системного вызова, который больше похож на 2000 тактов.

Мораль : используйте с осторожностью.

...