Ефективно умножаване на голям комплексен вектор със скаларен C++

В момента се опитвам да направя най-ефективно умножение на място на масив от комплексни числа (памет, подравнена по същия начин, по който би бил std::complex, но в момента използвам нашия собствен ADT) с масив от скаларни стойности, който е същият размер като масив от комплексни числа.

Алгоритъмът вече е паралелен, т.е. извикващият обект разделя работата на нишки. Това изчисление се извършва върху масиви от стотици милиони - така че може да отнеме известно време, за да завърши. CUDA не е решение за този продукт, въпреки че ми се иска да беше. Имам достъп до усилване и следователно имам известен потенциал да използвам BLAS/uBLAS.

Мисля обаче, че SIMD може да даде много по-добри резултати, но не съм достатъчно запознат с това как да направя това със сложни числа. Кодът, който имам сега, е както следва (не забравяйте, че това е разделено на нишки, които съответстват на броя на ядрата на целевата машина). Целевата машина също е неизвестна. Така че общият подход вероятно е най-добрият.

void cmult_scalar_inplace(fcomplex *values, const int start, const int end, const float *scalar)
{
    for (register int idx = start; idx < end; ++idx)
    {
        values[idx].real *= scalar[idx];
        values[idx].imag *= scalar[idx];
    }
}

fcomplex се дефинира, както следва:

struct fcomplex
{
    float real;
    float imag;
};

Опитах ръчно да развия цикъла, тъй като броят на окончателните ми цикли винаги ще бъде степен 2, но компилаторът вече прави това вместо мен (развих до 32). Опитах const float препратка към скалара - мислейки, че ще спестя един достъп - и това се оказа равно на това, което компилаторът вече правеше. Пробвах STL и transform, които играха близки резултати, но все още по-лоши. Опитах също да прехвърля към std::complex и да му позволя да използва претоварения оператор за scalar * complex за умножението, но това в крайна сметка доведе до същите резултати.

И така, някой с идеи? Изказваме голяма благодарност за отделеното време при обмислянето на това! Целевата платформа е Windows. Използвам Visual Studio 2008. Продуктът не може да съдържа и GPL код! Много благодаря.


person Keck    schedule 28.07.2011    source източник
comment
Ако използвате компилатора Intel ICC (който можете просто да включите към Visual Studio), тогава той трябва да може да векторизира такъв прост цикъл.   -  person Paul R    schedule 28.07.2011


Отговори (4)


Можете да направите това доста лесно със SSE, напр.

void cmult_scalar_inplace(fcomplex *values, const int start, const int end, const float *scalar)
{
    for (int idx = start; idx < end; idx += 2)
    {
        __m128 vc = _mm_load_ps((float *)&values[idx]);
        __m128 vk = _mm_set_ps(scalar[idx + 1], scalar[idx + 1], scalar[idx], scalar[idx]);
        vc = _mm_mul_ps(vc, vk);
        _mm_store_ps((float *)&values[idx], vc);
    }
}

Обърнете внимание, че values и scalar трябва да бъдат подравнени на 16 байта.

Или можете просто да използвате компилатора Intel ICC и да го оставите да свърши тежката работа вместо вас.


АКТУАЛИЗАЦИЯ

Ето една подобрена версия, която разгръща цикъла с коефициент 2 и използва единична инструкция за зареждане, за да получи 4 скаларни стойности, които след това се разопаковат в два вектора:

void cmult_scalar_inplace(fcomplex *values, const int start, const int end, const float *scalar)
{
    for (int idx = start; idx < end; idx += 4)
    {
        __m128 vc0 = _mm_load_ps((float *)&values[idx]);
        __m128 vc1 = _mm_load_ps((float *)&values[idx + 2]);
        __m128 vk = _mm_load_ps(&scalar[idx]);
        __m128 vk0 = _mm_shuffle_ps(vk, vk, 0x50);
        __m128 vk1 = _mm_shuffle_ps(vk, vk, 0xfa);
        vc0 = _mm_mul_ps(vc0, vk0);
        vc1 = _mm_mul_ps(vc1, vk1);
        _mm_store_ps((float *)&values[idx], vc0);
        _mm_store_ps((float *)&values[idx + 2], vc1);
    }
}
person Paul R    schedule 28.07.2011
comment
Те са подравнени по 16 байта. Направих това при по-ранна оптимизация на всички данни, върху които работихме. Отивам да опитам това. Оценявам много вашия отговор! - person Keck; 28.07.2011
comment
Кажете ми как върви - мисля, че вероятно мога да направя зареждането на vk по-ефективно, така че може да успеем да получим малко повече производителност. Имайки предвид това, има толкова малко операции по отношение на зарежданията и съхраняването, че може вече да сте ограничени в честотната лента на паметта. - person Paul R; 28.07.2011
comment
Уф ... същите средни времена, компилаторът вероятно вече прави това? Може да съм объркал нещо, въпреки че го накарах да компилира за MSVC 2008. - person Keck; 28.07.2011
comment
говори се, че ICC струва твърде скъпо за момента. И така, останах на MSVC 2008. - person Keck; 29.07.2011
comment
Няма значение, на различни машини времето е почти два пъти по-бързо с тази версия. Ще ми е интересно да разбера вашите оптимизации около vk!!! - person Keck; 29.07.2011
comment
@Keck: ICC е само около $500 - ако производителността е важна, тогава това е най-добрата печалба за долар, която ще получите толкова бързо, лесно и надеждно в сравнение с програмистите да пишат оптимизиран код на ръка. - person Paul R; 29.07.2011
comment
@Keck: Мисля, че vk вероятно може да се зарежда по-ефективно, като се използва 64-битово MMX зареждане, последвано от SSE инструкция за разбъркване, но все още не съм имал време да разгледам това подробно. - person Paul R; 29.07.2011
comment
Не мога да се съглася с вас повече за потенциалната загуба на пари само от това, че работя върху това дори няколко дни. Оценявам, че разглеждате това. Имаме много, много много функции, които отнемат много време, които са в подобна лопатка. И така, този подход, който се оказа плодотворен, трябва да ми позволи да внедря други функции, т.е. кръстосано произведение, fft преместване, fft претегляне, fft фазиране, умножение на матрици и т.н. Всички тези неща отнемат много време в момента. Така че всяко подобрение е доста голямо, като се имат предвид размерите на нашите набори от данни! - person Keck; 29.07.2011
comment
@Keck: ако искате да представите случай на TPTB, тогава можете да получите безплатна 30-дневна оценка на ICC от intel.com, да компилирате отново кода си с ICC и след това да го сравните с кода на MSVC. Това трябва да е доста убедителен аргумент. Късмет ! - person Paul R; 29.07.2011
comment
Това определено даде някои големи увеличения на скоростта на някои машини в сградата - някои от които са близки до спецификациите на най-големите ни клиенти. И така, интересувам се от всякакви други оптимизации, които може да имате около настройката на vk. Засега мисля, че пренаписването на повечето от нашите подобни функции в това отношение може да е най-доброто, което мога да направя за софтуера в момента. Странична бележка, за нашия най-малък размер, който е 4096*4096 или ~16 милиона, това премина от 48 милисекунди на 38 милисекунди. Така че, благодаря много! - person Keck; 29.07.2011
comment
@Keck: страхотно - радвам се, че помогна - ще погледна зареждането на vk и ще видя дали мога да го подобря, но може да достигате ограниченията на честотната лента на паметта предвид размера на вашия набор от данни. - person Paul R; 29.07.2011
comment
още 4ms от времето в най-лошия случай :) Благодаря много! - person Keck; 29.07.2011
comment
Е, бърза промяна на мнението. Вече мога да използвам MKL и TBB в тази програма ... Освен това казаха, че ще разгледат ICC в близко бъдеще. Не го видях да идва... - person Keck; 29.07.2011
comment
@Keck: Добре - това трябва да помогне с другите рутинни процедури, които искате да оптимизирате. Със сигурност много по-лесно/бързо от ръчното кодиране на SIMD. - person Paul R; 29.07.2011

Вашият най-добър залог ще бъде да използвате оптимизиран BLAS, който ще се възползва от всичко, което е налично на вашата целева платформа.

person David Heffernan    schedule 28.07.2011
comment
Склоня се да го пробвам и аз. Не съм запознат с BLAS извън обхвата на MKL - използва се на друг продукт. Така че се надявам, че този в усилване е по-ефективен от това, което правя. Нещата с лицензи LGPL, BOOST са потенциално възможни, но имаме строги изисквания за проследяване в допълнение към проблема с липсата на пари. - person Keck; 28.07.2011

Един проблем, който виждам, е, че във функцията е трудно за компилатора да разбере, че скаларният указател наистина не сочи в средата на комплексния масив (scalar на теория може да сочи към комплексната или реалната част на комплекса). Това всъщност налага реда на оценяване.

Друг проблем, който виждам, е, че тук изчислението е толкова просто, че други фактори ще повлияят на необработената скорост, следователно, ако наистина ви е грижа за производителността, единственото решение според мен е да внедрите няколко варианта и да ги тествате по време на изпълнение на потребителската машина, за да откриете кое е най-бързо.

Това, което бих обмислил, е използването на различни размери за развиване, както и игра с подравняването на scalar и values (моделът за достъп до паметта може да има голямо влияние върху кеширащите ефекти).

За проблема с нежеланата сериализация опция е да видите какъв е генерираният код за нещо подобно

float r0 = values[i].real, i0 = values[i].imag, s0 = scalar[i];
float r1 = values[i+1].real, i1 = values[i+1].imag, s1 = scalar[i+1];
float r2 = values[i+2].real, i2 = values[i+2].imag, s2 = scalar[i+2];
values[i].real = r0*s0; values[i].imag = i0*s0;
values[i+1].real = r1*s1; values[i+1].imag = i1*s1;
values[i+2].real = r2*s2; values[i+2].imag = i2*s2;

защото тук оптимизаторът има на теория малко повече свобода.

person 6502    schedule 28.07.2011
comment
това изпълнение също доведе до същите средни времена. Все пак разбирам какво искаш да кажеш. Оценявам отговора ви. Развих това до r4/i4/s4, когато опитах това. - person Keck; 29.07.2011
comment
Опитах много размери за развиване на примки. Дори до 32 и същите резултати се дават както в изолация, така и в продукт. Също така хванах грешка в това за настройката на s0, но както вероятно се досещате, това не оказа влияние върху времето :P - person Keck; 29.07.2011
comment
@Keck: хехе... оправено. Първоначално имаше i+0 навсякъде, премахнах грешната част от това (и между другото писането на такива неща на iPad е мъчение, очевидно Apple мрази програмистите). - person 6502; 29.07.2011
comment
достатъчно интересно е, че на най-добрата машина, която имаме в сградата, вашето внедряване наистина предлага увеличение на производителността. Изпратих си генерираното събрание и планирам да разгледам всички реализации тази вечер в опит за обучение :) Така че, аз също много оценявам вашата помощ, защото това е нов начин да се мисли за проблема, ако не друго :) - person Keck; 29.07.2011

Имате ли достъп до Integrated Performance Primitives на Intel? Интегрирани примитиви за производителност Те имат редица функции, които обработват случаи като това с доста прилично представяне. Може да имате известен успех с вашия конкретен проблем, но няма да се изненадам, ако вашият компилатор вече върши прилична работа за оптимизиране на кода.

person JoeD    schedule 28.07.2011
comment
Друг продукт има MKL/TBB/IPP в нашата компания. Аз също програмирам за този продукт, но този няма тези лицензи. И така, с пяна ми идва това да е решението, но не можем да го използваме за това заради финансирането :/ Така че е гадно да си много запознат с тези продукти, но да не можеш да ги използваш. Освен това се страхувам, че компилаторът вече прави почти всичко, което мога да измисля, за да направя с него за този подход. Иска ми се IPP да е опция, но за съжаление не е. - person Keck; 28.07.2011
comment
@Keck: Вече не се нуждаете от специален лиценз за IPP et al. Докато имате лиценз за ICC компилатора, това обхваща разпространението на двоични файлове, свързани с IPP и т.н. - person Paul R; 28.07.2011
comment
@Paul R И така, според вас, ако аз, разработчикът, имам лиценз чрез моята компания, можем да го използваме на друг продукт? Някои отговорни хора ми казаха, че не е така. - person Keck; 28.07.2011
comment
@Keck: лицензирането се промени преди няколко години, когато започнаха да обединяват IPP, MKL и т.н. с професионалното издание на ICC компилатора. Докато имате валиден лиценз за самия ICC, тогава всички продукти, които създавате, няма да имат проблеми с лицензирането, дори ако се свързват с IPP или други библиотеки на Intel. IANAL разбира се, така че проверете отново лиценза на Intel и може би говорете с правния си отдел, но аз съм 99,9% сигурен в това. - person Paul R; 29.07.2011
comment
IPP няма комплексно/скаларно умножение. - person jiveturkey; 04.02.2020