2015-09-23 3 views
2

Задача:OpenCL, сумма векторных элементов целочисленных

В OpenCL 1.2, там нет встроенной функции, как

long sum_int4_elements_into_long(int4 v); 

Что я пробовал:

Так Я использую код ниже (предварительно загруженная внутренняя часть развернутого цикла)

// acc is long 
int4 tmp=arr[i+7]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i+6]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i+5]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i+4];" 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i+3]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i+2]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i+1]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w; 
tmp=arr[i]; 
acc+=tmp.x+tmp.y+tmp.z+tmp.w;  

суммировать (уменьшить) все элементы целочисленного массива (int4 arr) в одну длинную переменную с ускорением только от +% 20 до +% 30 по сравнению с серийным кодом. Если бы он мог включить SSE или AVX, это было бы намного быстрее.

также попыталось:

с использованием чистого целочисленного аккумулятора ускоряет операцию суммирования вверх 3x, но целочисленные переполнения, так что я могу использовать только длинную переменный. Тогда я попытался с помощью long4 и long2 переменных как

// acc is a long4, arr is an int4 array 
    acc+=convert_long4(arr[...]) 

    // acc is a long2, arr is an int2 array 
    acc+=convert_long2(arr[...]) 

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

Разъяснение:

Там должен быть какой-то эквивалент SIMD (SSE или AVX) Инструкция по сборке AMD и INTEL для

 32 bit integers in a 128-bit SSE register 
     |  |  |  |   
    acc+=tmp.x + tmp.y + tmp.z + tmp.w 
^ 
    | 
    64-bit register 

но почему-то OpenCL оленья кожа есть отображение этого или я не укладывалось C99 достаточно хорошо, поэтому cl-компилятор не может использовать инструкции SSE/AVX.

Ближайшие встроенный поплавковые версии

acc=dot(v,(float4)(1,1,1,1)); 

, но мне нужна целочисленная версия этого, потому что FP нуждается Суммирование коррекции Кагана, который нуждается в дополнительное время.

Edit:

Я не уверен, если ИНТ ИНТ ИНТ ИНТ будет иметь надлежащий длинный результат или просто иметь переполнения Int в длинный.

OpenCL версии: 1.2 работает на CPU (Java ----> Jocl)

CPU: AMD FX-8150

ОС: 64-разрядная версия Windows 10

Driver: последние один на драм ,

Дата: 23.09.2015

Для сравнения:

16М 32-битных чисел, FX8150 @ 3300MHz (с использованием 6 ядер из 8)

серийный код на Java 1.8_25 занимает 16,5 мс в среднем.

IntStream из Java-1.8 занимает 13,5 мс в среднем (X.reduce (0, Integer :: сумма))

Параллельный код в этом вопросе занимает 12,5 мс в среднем (с использованием одного рабочую группу)

Параллельный код в этом вопросе занимает 5,8 мс в среднем на (с использованием четырех рабочих групп)

Parallel но перелива недлинного версия принимает 5мс. (Ударяя ПСП)

МФА Ответ:

acc=dot(v,(double4)(1,1,1,1)); 

занимает 13,5 мс в среднем, но всплывают версия занимает 12,2 мс в среднем.

Я не уверен, что поплавок может всегда сохранять свою точность, чтобы добавить 1.0 (или даже 0.0) к очень большому числу fp.

+0

http://stackoverflow.com/questions/10811413/sum-vector-components-in-opencl-sse-like –

ответ

2

Какова скорость выполнения сокращений? Возможно, это не так уж плохо.

long4 lv = (long4)v; 
long2 t = lv.xy + lv.zw; 
acc += t.x + t.y; 

Кроме того, если вы действительно хотите уменьшить количество элементов, а не один int4.Затем суммируйте их в long4 пространстве, а затем уменьшите только последний.

long4 sums = long4(0); 
sums += convert_long4(arr[0]); 
sums += convert_long4(arr[1]); 
sums += convert_long4(arr[2]); 
... 
sums += convert_long4(arr[N-1]); 
sums += convert_long4(arr[N]); 
long2 t = sums.xy + sums.zw; 
long res = t.x + t.y; 

ПРИМЕЧАНИЕ: Если это единственная операция, которую делают узкие места памяти, вероятно, основной проблемой здесь. Поэтому измерение времени выполнения ядра приведет к очень предвзятому результату.

+0

Я попробовал это сначала, функция convert_long4() не работает в моей системе и отключает приложение. Может быть, из-за неисполнения драйверов id. –

+0

Но вещь .xy + .zw должна быть лучше. Я попробую это. Mine должно быть 4 инструкции, а ваше должно быть только 2. –

+0

tmp = convert_long4 (arr [j]); t = tmp.xy + tmp.zw; acc + = t.x + ty; "\t взял в среднем 6,7 мс, используя восемь рабочих групп, которые медленнее, чем другие решения, и которые, по-видимому, являются узкими местами для части convert_long4. Без этого код не будет компилироваться. –

1

Сумма в 4 цеха будет вписываться в поплавок с двойной точностью. Попробовали ли вы попробовать?

double acc; 
    acc=dot(v,(double4)(1,1,1,1)); 

Не могли бы вы указать сроки для этого тоже, пожалуйста?

EDIT: добавлено больше информации.

Double версия взяла 13,2 мс в среднем в то время как поплавок версия заняла 12,2 мс в среднем, но я не уверен, если поплавок дополнение сохраняет квантовых шагов INTEGERS' всегда. Может ли его precisiton при больших поплавках быть достаточно для , чтобы добавить 1.0 или 0.0?

Дополнительная точность может определенно добавить дополнительные 1 мс. На каком-то более раннем оборудовании AMD GPU двойные операции занимают в два раза больше, потому что они фактически используют два регистра с плавающей запятой. Небольшое снижение производительности, которое вы измеряете, также может быть учтено, если учесть, что математически говоря, операция двойной точности состоит из 8 отдельных операций с одиночной точностью. В целом, я думаю, что ваш процессор делает приличную работу с удвоениями по сравнению с поплавками.

Одиночная точность не будет переполняться, если ваша сумма остается менее 24 бит. More about this here. Удваивает 54 бита точности (here). Возможно, стоит иметь отдельное «маленькое» ядро, когда вы знаете, что сумма будет небольшой?

+0

Хорошо, я преобразую ints в double (в ядре) для этого и запросит тайминги как можно скорее. –

+0

Двойная версия в среднем составляла 13,5 мс, в то время как версия с плавающей точкой в ​​среднем составляла 12,2 мс, но я не уверен, что добавление с плавающей точкой всегда сохраняет квантовые шаги целых чисел. Может ли его precisiton при больших поплавках быть достаточным для добавления 1.0 или 0.0? –

+0

У меня уже есть небольшое ядро, но мне тоже нужен большой. Если бы я мог использовать удвоение быстрее, чем это, я добавлю корректировку Kahan Summation для сумматора с плавающей запятой. (сделает это еще медленнее) https://en.wikipedia.org/wiki/Kahan_summation_algorithm, что мне изначально нужно, - это операции SIMDify, которые не занимают больше времени tmp.x + tmp.y + tmp.z + tmp.w. В любом случае, спасибо. –

Смежные вопросы