Почему StD :: уменьшить потребность коммутативности

голоса
5

https://en.cppreference.com/w/cpp/algorithm/reduce

Это говорит о том, что поведение операции не определен, если операция не является коммутативной, но почему? Мы просто разделить массив на блоки, а затем объединить результат, это только необходимо ассоциативности?

Задан 13/02/2020 в 23:51
источник пользователем
На других языках...                            


3 ответов

голоса
1

Поведение на самом деле недетерминировано , если операция между операндами не является коммутативной. «недетерминирована» не то же самое , как «неопределенный». С плавающей точкой математике не является коммутативной, например. Это одна из причин , почему вызов std::reduceне может быть детерминированным, потому что бинарная функция применяется в произвольном порядке.

Обратитесь к настоящей записке в стандарте:

Примечание: Разница между reduceи в accumulateтом , что касается уменьшения binary_opв произвольном порядке, что дает недетерминированный результат для неассоциативного или некоммутативного binary_op , таких как с плавающей точкой дополнение. -End примечание]

Ответил 14/02/2020 в 00:02
источник пользователем

голоса
1

Стандарт определяет обобщенную сумму следующим образом : numeric.defns

Определить GENERALIZED_NONCOMMUTATIVE_SUM (оп, а1, ..., ап) следующим образом:

  • а1, когда N = 1, в противном случае

  • оп (GENERALIZED_NONCOMMUTATIVE_SUM (оп, a1, ..., Ak), оп (GENERALIZED_NONCOMMUTATIVE_SUM (оп, аМ, ..., ап)) для любого K где 1

Определить GENERALIZED_SUM (оп, а1, ..., ап) в GENERALIZED_NONCOMMUTATIVE_SUM (оп, b1, ..., Bn), где b1, ..., Bn может быть любая перестановка a1, ..., ап.

Таким образом, порядок суммирования, а также порядок операндов не определен. Таким образом, если бинарная операция не является коммутативной или не ассоциативно, то результат не определен.

Это также явно указано здесь .

Относительно того, почему: Это дает библиотека поставщиков больше свободы, так что они могут или не могут реализовать его лучше. В качестве примера , где реализация может извлечь выгоду из коммутативности. Рассмотрим сумму a+b+c+d+e, мы сначала вычислить a+bи c+dпараллельно. Теперь a+bвозвращается прежде , чем c+dделает (как это может произойти, потому что это делается параллельно). Вместо того , чтобы ждать возвращаемого значения c+dтеперь мы можем непосредственно вычислить , (a+b)+eа затем добавить этот результат к результату c+d. Таким образом , в конце концов, мы рассчитали ((a+b)+e)+(c+d), что перегруппировка a+b+c+d+e.

Ответил 14/02/2020 в 00:07
источник пользователем

голоса
6

std::reduceтребует как ассоциативности и коммутативности. Ассоциативность явно необходимо для параллельного алгоритма, так как вы хотите, чтобы выполнить расчет на отдельные куски, а затем объединить их.

Что касается коммутативности: Согласно с Reddit пост по MSVC разработчиков STL Билли О'Нил, это необходимо для того , чтобы векторизации инструкции SIMD:

Перестановочность также необходимо включить векторизации, так как код, который вы хотите для снижения, чтобы выйти, как что-то вроде:

vecRegister = load_contiguous(first);
while (a vector register sized chunk is left) {
    first += packSize;
    vecRegister = add_packed(load_contiguous(first), vecRegister);
}
// combine vecRegister's packed components

и т.д., которые даны Интс и регистры SSE и A * B * C * D * E * F * G * ч дает что-то подобное (а * е) * (б * е) * (с * г) * (д * ч ).

Большинство других языков не делают явные вещи, чтобы сделать векторизации возможным их сокращение. И ничего не говорит, что мы не можем добавить noncommutative_reduce или что-то подобное, что в будущем, если кто-то приходит с убедительным прецедентом.

Ответил 14/02/2020 в 00:13
источник пользователем

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more