Опубликован: 30.05.2014 | Уровень: для всех | Доступ: платный | ВУЗ: Нижегородский государственный университет им. Н.И.Лобачевского

Самостоятельная работа 4: Оптимизация расчетов на примере задачи вычисления справедливой цены опциона Европейского типа

Версия 8. Оптимизация работы с кэш-памятью

В коде функций GetOptionPricesVX() мы работаем с 4-мя массивами (pT, pK, PS0, pC). При этом 3 из них используются только для чтения, а один (pC) для записи. Обратим внимание на тот факт, что значения, которые мы записываем в длинный массив pC, в цикле никак не используются. Таким образом, их кэширование вряд ли имеет смысл (массив pC относится к nontemporal data). Для записи напрямую в память, минуя кэш, результатов вычислений, идентифицированных нами как nontemporal data, предназначены так называемые streaming stores, использование которых приводит к уменьшению накладных расходов. В данном конкретном примере мы видим, что цикл имеет огромное число итераций, в нем выполняется сравнительно мало арифметических операций относительно операций с памятью, на Xeon Phi одновременно работают сотни потоков. Совокупность этих факторов приводит к тому, что мы прокачиваем через шину объем данных, сравнимый, а то и превосходящий ее пропускную способность. Использование streaming stores для массива pC позволит нам сэкономить по одной операции чтения, нужной для поддержания системы кэш-памяти в согласованном состоянии, на каждой итерации. Проверим этот факт.

__declspec(noinline) void GetOptionPricesV8(float *pT, 
  float *pK, float *pS0, float *pC)
{
  int i;
  float d1, d2, erf1, erf2, invf;
  float sig2 = sig * sig;

#pragma simd
#pragma vector nontemporal
#pragma omp parallel for private(invf, d1, d2, erf1, erf2)
  for (i = 0; i < N; i++)
  {
    invf = invsqrtf(sig2 * pT[i]);
    d1 = (logf(pS0[i] / pK[i]) + (r + sig2 * 0.5f) * 
         pT[i]) * invf;
    d2 = (logf(pS0[i] / pK[i]) + (r - sig2 * 0.5f) * 
         pT[i]) * invf;
    erf1 = 0.5f + 0.5f * erff(d1 * invsqrt2);
    erf2 = 0.5f + 0.5f * erff(d2 * invsqrt2);
    pC[i] = pS0[i] * erf1 - pK[i] * expf((-1.0f) * r *
            pT[i]) * erf2;
  }
}

Добавьте в массив указателей GetOptionPrices новую функцию.

tGetOptionPrices GetOptionPrices[9] = 
{ GetOptionPricesV0, GetOptionPricesV1, GetOptionPricesV2,
  GetOptionPricesV3, GetOptionPricesV4, GetOptionPricesV5,
  GetOptionPricesV6, GetOptionPricesV7, GetOptionPricesV8
};

Соберите программу и проведите эксперименты. На описанной ранее инфраструктуре авторы получили следующие времена.

Таблица 9.17. Время работы на сопроцессоре Xeon Phi версии 8 (в секундах), 60 ядер
N 60 000 000 120 000 000 180 000 000 240 000 000
Версия V8 0,009 0,018 0,027 0,035
S(V6.3/V8) 46,878 47,505 47,610 47,832
Таблица 9.18. Время работы на сопроцессоре Xeon Phi версии 8 (в секундах), 120 ядер
N 60 000 000 120 000 000 180 000 000 240 000 000
Версия V8 0,007 0,013 0,019 0,026
S(V6.3/V8) 63,873 65,048 65,426 65,887
Таблица 9.19. Время работы на сопроцессоре Xeon Phi версии 8 (в секундах), 240 ядер
N 60 000 000 120 000 000 180 000 000 240 000 000
Версия V8 0,007 0,013 0,019 0,026
S(V6.3/V8) 63,222 64,768 65,379 65,420

Как видно из табл. 9.17-9.19, времена работы версии 8 по сравнению с версией 7.1 ухудшились на 60 ядрах и улучшились на 120 и 240. Ускорение на 120 и 240 потоках также выросло.

Наконец, для сравнения приведем результаты работы версии 8 на центральном процессоре (16 ядер) и сопроцессоре (120 ядер).

Таблица 9.20. Сравнение времени работы версии 8 (в секундах)
N 60 000 000 120 000 000 180 000 000 240 000 000
Xeon 0,030 0,061 0,090 0,116
Xeon Phi 0,007 0,013 0,019 0,026

Как видно из таблицы 9.20, время работы параллельной версии с прогревом на сопроцессоре примерно в 4,6 раза лучше, чем на центральном процессоре.

Также отметим, что применения прагмы vector nontemporal почти не ускоряет код на центральном процессоре (сравните времена версий 7.1 и 8), что вполне объяснимо – используется всего 16 потоков, шина не перегружается.

Дополнительные задания

  • Ответить на вопросы в тексте.
  • Выяснить, какой способ организации данных в данной задаче более эффективен.
  • Изменить реализацию так, чтобы она производила одновременное вычисление опционов типа Call и Put. Провести эксперименты с разными версиями кода, проверить влияние техник оптимизации на время работы последовательной и параллельной версий для Xeon и Xeon Phi, привести выкладки, подтверждающие факт перегрузки шины на Xeon Phi при использовании значительного числа потоков.
  • Провести эксперименты на Xeon Phi с разным числом потоков. Выяснить, какое число потоков является оптимальным.
Svetlana Svetlana
Svetlana Svetlana

Здравствуйие! Я хочу пройти курс Введение в принципы функционирования и применения современных мультиядерных архитектур (на примере Intel Xeon Phi), в презентации самостоятельной работы №1 указаны логин и пароль для доступ на кластер и выполнения самостоятельных работ, но войти по такой паре логин-пароль не получается. Как предполагается выполнение самосоятельных работ в этом курсе?

Егор Кузьмин
Егор Кузьмин
Россия, г. Москва
Вера Борисова
Вера Борисова
Россия