LINUX.ORG.RU

Помогите с числодробилкой

 


0

3

Всем привет. Есть код, который, который делает вот так:

for (unsigned int k = 0; k < 50000; ++k) {

            float curSample = (float)samples.get()[k];
            complexSamples.get()[2 * k] = mycos((float)k*K)*curSample;
            complexSamples.get()[2 * k + 1] = mysin((float)k*K)*curSample;
        }

И это выполняется ну ооочень долго.

Если делать так:

inline float mysin(const float& x) {
    float x3 = x*x*x;
    return x - x3/6 + x3*x*x/120 - x3*x3*x/5040;
}

inline float mycos(const float& x) {
    float _sin = mysin(x);
    return sqrt(1-_sin*_sin);
}

, то время более-менее сокращается, но опять же не устраивает.

Табличный метод почему-то работает медленнее серии Тейлора.

Хочется, чтобы этот код мог вызываться по 500 раз в секунду и успевать работать долгое время на Intel Xeon 1220l v3.

★★

почему бы не развернуть mycos и не использовать уже вычисленное значение синуса?

Harald ★★★★★
()

Дожили, что синус можно посчитать только на xeon и в цикле с десятками тысяч итераций вызывать какие-то безумные get()

anonymous
()

float curSample = (float)samples.get()[k];

Прототип функций в студию. Да и лучше поместить получение массива до цикла

Openmp?

AlexVR ★★★★★
()

(float)samples.get()[k];

присоеденюсь к ув.anonymous :-)

и ещё: mycos,mysin были хороши в игрушках во времена Кормака, до граф.процессоров и прочих cuda

MKuznetsov ★★★★★
()
Ответ на: комментарий от anonymous

А еще эту тему уже трое отслеживают. Видимо вопрос то наболевший.

deep-purple ★★★★★
()
Ответ на: комментарий от MKuznetsov

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

Сейчас уже не могу прототип функции написать, но могу сказать, что samples имеет тип std::shared_ptr<int>

Dikar ★★
() автор топика
Ответ на: комментарий от MKuznetsov

Да, спасибо. Еще хочу анонимно добавить, что можно держать в памяти высчитанную таблицу синусов и вообще почти ничего не вычислять в рантайме.

anonymous
()
Ответ на: комментарий от spider_russia

Исследуем сигнал с высокой частотой дискретизации

Dikar ★★
() автор топика
Ответ на: комментарий от Dikar

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

anonymous
()
Ответ на: комментарий от Dikar

std::shared_ptr<int>

Фак мой анонимный мозг. Только в бусте шаред_птр без дополнительных танцев с бубном умеет удалять массивы, а если это просто с++11, то будет освобождаться только 0вой элемент. Все, хватит говнокод писать, дай пива спокойно попить!

anonymous
()
Ответ на: комментарий от CyberK

complexSamples это std::unique_ptr<float>.

И плиз не стоит гнать на умные указатели-на 4 строки выше такой-же код достаточно быстро работает без тригонометрии.

Dikar ★★
() автор топика
Ответ на: комментарий от anonymous

Бинго!) Gprof указывает на эту функцию, а комментирование двух строк с тригонометрией ускоряет выполнение на один порядок

Dikar ★★
() автор топика
Ответ на: комментарий от Dikar

Если константа, то предвычисляешь синусы-косинусы, выйдет всего-то на 200 Кб.

for (unsigned k = 0; k < 50000; k++) {
    cosTable[k] = cosf((float)k*K);
    sinTable[k] = sinf((float)k*K);
}

mix_mix ★★★★★
()
Ответ на: комментарий от mix_mix

Спасибо! Завтра попробую! Жаль, что сам не догадался(((

Dikar ★★
() автор топика
Ответ на: комментарий от Harald

Действительно, получается, что 2 раза вычисляется sin и затем по результату второго его вычисления уже вычисляется cos. Почему б сразу тогда не сделать что-то вроде?

_sin = mysin((float)k*K)
complexSamples.get()[2 * k] = sqrt(1-_sin*_sin)*curSample;
complexSamples.get()[2 * k + 1] = _sin*curSample;
Потом уже можно и о распараллеливании подумать.

P.S.
k и K действительно разные или это опечатка?

grem ★★★★★
()
Последнее исправление: grem (всего исправлений: 1)
Ответ на: комментарий от Dikar

= mycos((float)k*K)*curSample;

= mysin((float)k*K)*curSample;

кстати думается, что вы какое-то ЗЛО задумали :-) дальше то вы куда их деваете ? это-ж sin и cos от одного и того-же...

далее шедевр:

inline float mysin(const float& x) {
    float x3 = x*x*x;
    return x - x3/6 + x3*x*x/120 - x3*x3*x/5040;
}

inline float mycos(const float& x) {
    float _sin = mysin(x);
    return sqrt(1-_sin*_sin);
}
а ничё что mysin вызывается ДВАЖДЫ?

MKuznetsov ★★★★★
()

x - x3/6 + x3*x*x/120 - x3*x3*x/5040;

А теперь подставь x = 4 и посмотри чему получилось значение синуса, оно по модулю вышло заметно больше 1 (то есть -1.3841269841). И при увеличении x продолжает быстро расти. Да и корень от отрицательного числа не очень хорошо выглядит. Если уж используешь разложение далеко от x = 0, то членов разложения должно быть заметно больше. Если вообще такое прокатит с произвольным большим x. Тогда уж раскладывай вблизи нуля для определённых значений, а дальше используй свойство периодичности этой функции для разных диапазонов x.

grem ★★★★★
()
Последнее исправление: grem (всего исправлений: 4)
Ответ на: комментарий от grem

Почему просто не использовать встроенную функцию? Вычисления синуса и косинуса для 50000 значений на core i3 занимают меньше 0.01 секунды. Но косинус (синус) всё равно лучше вычислять по вычисленному значению синуса (косинуса) - так быстрее будет в 1.5-2 раза.

grem ★★★★★
()
Последнее исправление: grem (всего исправлений: 3)
Ответ на: комментарий от grem

Для 50000 это 0.01 на i3, мне же надо вычислять за секунду такое 1000 раз на процессоре 1.2ггц, при том, что остальную нагрузку в виде приема всего этого по сети и дальнейшую обработку никто не отменял.

Dikar ★★
() автор топика
Ответ на: комментарий от MKuznetsov

Да, мой косяк в вечернем коде. mix_mix уже предложил прекрасное решение, завтра опробую.

Dikar ★★
() автор топика

Что такое большая К? И вообще дай пожалуйста кусок кода который можно просто скомпилить, со всеми недостающими кусками. Я хочу его запрофайлить и посмотреть как можно ускорить

CyberK
()
Ответ на: комментарий от rogerw

Я сам все как-то по сетям, да по гуям работал. Не думал, что удосужится после института вспоминать про косинусы, фазы и амплитуды

Dikar ★★
() автор топика
Ответ на: комментарий от CyberK

Большая K это 2*M_PI*33/50000.

Всю остальную инфу по типам я выше писал впринципе. Завтра только смогу код скинуть.

Dikar ★★
() автор топика
Ответ на: комментарий от anonymous

Ути-пути, какая попоболь читается в этой нелепейшей фразе из классики 90х, малыш;)

Dikar ★★
() автор топика
Ответ на: комментарий от Dikar

Это на одном ядре, можно попытаться распараллеливать через openmp, это очень легко, если вычисления независимые.

В любом случае, синус у тебя неправильно считается и если уж косинус всё-таки считать через корень, то нужно учитывать изменение знака косинуса.

grem ★★★★★
()
Ответ на: комментарий от grem

Пордон за нубский вопрос, но разве openmp позволяет эффективно парралелить на CPU? У меня нет видеокарты

Dikar ★★
() автор топика

Есть еще в сях такая функция sincos, в процессоре даже есть инструкция FSINCOS под это дело, правда она через сопроцессор работает

SZT ★★★★★
()
Ответ на: комментарий от Dikar

Умеет, умеет. Не знаю насколько эффективно, но сейчас у тебя более актуальная задача - исправить неправильное вычисление тригеометрических функций.

grem ★★★★★
()

Табличный метод почему-то работает медленнее серии Тейлора

Не понял, таблица множителей sin((float)k*K) и cos((float)k*K) подготовленная заранее, и цикле только домножение на curSample работает медленнее?

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от SZT

Опередили:-)

Конечно sincos.

Разложение в Тейлор хорошо только если не нужна высокая точность и аргумент мал.

Табулирование актуально только если спектр згачений аргцмента ограничен.

А вообще тут попахивает фурьем... надо тс-у глянуть как сделан FFT. И м.б. взять готовую билиотеку а не строить велосипед.

AIv ★★★★★
()
Ответ на: комментарий от no-such-file

Нет. На stackoverflow нашел табличный синус на 65536 предварительно рассчитанных синусов. Так вот выборка из этой таблицы работает медленнее, чем серия Телйлора.

Только что попробовал способ метод mix_mix и все заработало как надо, без затыков. Жаль, что не догадался сам.

Но у меня осталось еще кучка-другая математики, там и поработаю с OpenMP

Dikar ★★
() автор топика

Как минимум, можно сделать вот так, исходя из твоих «быстрых» формул.

for (unsigned int k = 0; k < 50000; ++k) {

            float curSample = (float)samples.get()[k];
            complexSamples.get()[2 * k] = mycos((float)k*K)*curSample;
            complexSamples.get()[2 * k + 1] = mysin((float)k*K)*curSample;
        }
for (unsigned int k = 0; k < 50000; ++k) {

            float curSample = (float)samples.get()[k];
            float tsin = mysin((float)k*K);
            ЧТОТАМУТЕБЯ obj = complexSamples.get();
            obj[2 * k] = (1-tsin*tsin)*curSample;
            obj[2 * k + 1] = tsin*curSample;
        }

Aswed ★★★★★
()

Векторизуй или посмотри что есть в IPP

yoghurt ★★★★★
()

А разве через синус/косинус суммы нельзя как-нибудь ускорить? Тут К фиксированно, увеличивается только k. Стал быть следующее значение это sin(kK + K), значения sin(kK) и cos(kK) можно взять из предыдущего, а sin/cos(K) тут константы.

ebantrop
()
Ответ на: комментарий от Aswed

Спасибо огромное, но я уже рассчитал таблицы синусов и косинусов для всех 50000 аргументов и просто делаю из них выборку.

Ну и да, если уж ты собрался оптимизировать вызов unique_ptr::get(), то нужно уменьшить количество вызовов не в 2 раза, а в 50000

[code = cxx] int *samplesPtr = samples.get(); float *complexResults = complexSamples.get(); for (unsigned int k = 0; k < 50000; ++k) { float tsin = mysin((float)k*K); complexResults[2 * k] = (1-tsin*tsin)*samplesPtr[k]; complexResults[2 * k + 1] = tsin*samplesPtr[k]; }

Хотя, это довольно глупая затея, которая не дает и процента производительности

Dikar ★★
() автор топика

Табличный метод почему-то работает медленнее серии Тейлора.

Потому, что кешлайн нифига не резиновый.

andreyu ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.