Временные ряды - C алгоритм оптимизации - PullRequest
0 голосов
/ 21 апреля 2020

Я работаю над проблемой, когда хочу собрать временные ряды, записанные в разных местах, и извлечь когерентный сигнал. Поднятие тяжестей выполняется в C с помощью оболочки Python для обеспечения более дружественного интерфейса. Я достиг точки, когда я удовлетворен теоретической правильностью алгоритма и хотел бы максимально оптимизировать его. Я достаточно понимаю C, чтобы написать что-то, что работает и распараллеливается с openMP, но не намного дальше этого.

Оптимизация проблемы важна, так как я имею дело с большими наборами данных - до 200 временных рядов в стеке частота дискретизации до 1000 Гц, порядок записи от нескольких месяцев до нескольких лет. Обработка может длиться от нескольких дней до нескольких недель с разумными вычислительными возможностями. Я выполняю этот шаг стекирования на кусках непрерывного временного ряда, чтобы не забивать память.

У меня есть несколько вопросов:

  1. Есть ли что-то очевидное, что мне не хватает что поможет (оптимизация с помощью компилятора, оптимизация алгоритма)?

  2. Самый значительный выигрыш, достигнутый к настоящему времени, был достигнут с флагом оптимизации -Ofast - я прочитал и просто хотел понять немного больше почему это быстрее и является ли это "безопасным" для моих целей?

  3. Куда (помимо траления через SO) я должен обратиться, чтобы узнать больше о такого рода проблемы? У меня есть другие проблемы в моем исследовании, которые я бы хотел решить, используя C!

Алгоритм

Я собираю временные ряды из каждого места непрерывно во времени в трехмерном сетчатом объеме. После того, как полный стек закончен для данной ячейки, мне нужно возвести в степень результат и нормализовать по количеству участвующих временных рядов.

#define MAX(a,b) (((a)>(b))?(a):(b))

EXPORT void migrate(double *sigPt, int32_t *indPt, double *mapPt, int32_t fsmp, int32_t lsmp, int32_t nsamp, int32_t nstation, int32_t avail, int64_t ncell, int64_t threads)
{
    double  *stnPt, *stkPt, *eStkPt;
    int32_t *ttpPt;
    int32_t ttp;
    int32_t to, tm, st;
    int64_t cell;

    #pragma omp parallel for private(cell,stkPt,eStkPt,ttpPt,st,ttp,tm) num_threads(threads)
    for (cell=0; cell<ncell; cell++)
    {
        stkPt = &mapPt[cell * (int64_t) nsamp];
        eStkPt = &mapPt[cell * (int64_t) nsamp];
        ttpPt = &indPt[cell * (int64_t) nstation];
        for(st=0; st<nstation; st++)
        {
            ttp   = MAX(0,ttpPt[st]);
            stnPt = &sigPt[st*(fsmp + lsmp + nsamp) + ttp + fsmp];
            for(tm=0; tm<nsamp; tm++)
            {
                stkPt[tm] += stnPt[tm];
            }
        }
        for(tm=0; tm<nsamp; tm++)
        {
            eStkPt[tm] = exp(stkPt[tm] / avail);
        }
    }
}

В настоящее время я компилирую с:

gcc -shared -fPIC -std=gnu99 ./source.c -fopenmp -Ofast -lm -o ./output

Я прочитал:

Профилирование python C расширения

Какие флаги и методы оптимизации G CC безопасны для разных процессоров?

среди прочих. Извинения, если я повторяю вопрос / мой вопрос плохо определен.

1 Ответ

0 голосов
/ 24 апреля 2020

Есть ли что-то очевидное, чего мне не хватает, что поможет (оптимизация с помощью компилятора, оптимизация алгоритма)?

Предлагаемый код кажется довольно хорошим (G CC должен уметь чтобы векторизовать это, и распараллеливание кажется нормальным). Но вот несколько возможных советов по улучшению производительности:

  • exp(stkPt[tm] / avail) может быть переписано в возможно более быстрое выражение pow(availFactor, stkPt[tm]) с availFactor константой, определенной вне l oop и установленной в exp(1.0 / avail). В соответствии с предложением @ tim18, вы также должны проверить, что это l oop векторизовано, потому что экспоненциальные вычисления обычно медленны.
  • вы можете попробовать использовать флаг компилятора -march=native, чтобы сделать код немного быстрее за счет менее переносимого двоичного файла (если вы не хотите терять переносимость для старых процессоров, вы можете попробовать -mtune=native, что обычно не так хорошо, как -march=native). Что касается вашего процессора, эта опция может позволить G CC использовать инструкции AVX и FMA (доступные на относительно недавних процессорах), которые должны ускорить ваш код. В качестве альтернативы вы можете включить такие функции вручную с помощью -mavx и -mfma.
  • . Вы можете попытаться адаптировать свой алгоритм так, чтобы горячий l oop, содержащий stkPt[tm] += stnPt[tm], работал в основном на данных в кеше. Этот момент очень важен. Действительно, эта самая горячая часть алгоритма, вероятно, связана с памятью. Хорошей отправной точкой является создание листов (например, при одновременной работе с 2 или 4 nstation).
  • Рассмотрите возможность работы с типами с плавающей точкой простой точности, а не с двойной точностью, если в результате Точность достаточно хороша.

Не забудьте проверить результаты, так как эти оптимизации могут повлиять на точность вашего кода!

Самый значительный выигрыш, достигнутый к настоящему времени, был достигнут с флаг оптимизации -Ofast - я прочитал и хотел немного больше понять, почему это быстрее и «безопасно» ли это для моих целей?

Использование -Ofast небезопасно. Действительно, эта опция включает -ffast-math, которая включает другие опции, такие как -funsafe-math-optimizations или -ffinite-math-only. Таким образом, ваш код не должен иметь дело со значениями NaN, а бесконечность и точность вычислений могут быть значительно ниже. Это может или не может быть проблемой, касающейся ваших ожиданий. Обратите внимание, что эта опция помогает G CC ускорить экспоненциальные вычисления (благодаря векторизации).

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