Skip to content

Latest commit

 

History

History
197 lines (144 loc) · 15.5 KB

apparatnaya_chast.md

File metadata and controls

197 lines (144 loc) · 15.5 KB

Аппаратная часть

Чем больше углубляешься в компьютер, тем больше теряешь соприкосновение с действительностью.

Харуки Мураками "Подземка"

Параллелизм на уровне инструкций

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

Данный уровень параллелизма эффективен в случае если вы хотите выжать максимум из своего железа. Однако, код после этого становится не переносимым.

Иерархия памяти

В большинстве современных ПК рассматривается следующая иерархия памяти:

  • Регистры процессора, организованные в регистровый файл — наиболее быстрый доступ (порядка 1 такта), но размером лишь в несколько сотен или, редко, тысяч байт.
  • Кэш процессора 1го уровня (L1) — время доступа порядка нескольких тактов, размером в десятки килобайт
  • Кэш процессора 2го уровня (L2) — большее время доступа (от 2 до 10 раз медленнее L1), около полумегабайта или более
  • Кэш процессора 3го уровня (L3) — время доступа около сотни тактов, размером в несколько мегабайт (в массовых процессорах используется недавно)
  • ОЗУ системы — время доступа от сотен до, возможно, тысячи тактов, но огромные размеры в несколько гигабайт, вплоть до сотен. Время доступа к ОЗУ может варьироваться для разных его частей в случае комплексов класса NUMA (с неоднородным доступом в память)
  • Дисковое хранилище — многие миллионы тактов, если данные не были закэшированны или забуферизованны заранее, размеры до нескольких терабайт
Операция Такты Время мс Отношение
Один такт процессора с частотой 3 ГГц 1
Обращение в кэш перого уровня L1 0.5
Неверно предсказанный переход 5
Обращение в кэш второго уровня L2 7 14x L1
Захват мьютекса 100
Обращение в оперативную память 100 20x L2 , 200x L1
Snappy сжатие 1 Кбайта данных 3000
Передача 2 Кбайт через 1 Гбит 10000 0.01
Прочитать произвольные 4 Кбайт данных с SSD 150000 0.15
Прочитать последовательно 1 Мбайт данных из памяти 250000 0.25
Прочитать последовательно 1 Мбайт данных с SSD 1000000 1 4x память
Прочитать последовательно 1 Мбайт данных из сети 10000000 10
Прочитать последовательно 1 Мбайт данных с диска 20000000 20 80x память, 40х SSD

Мультипроцессорные системы

Использование пары или большего количества физических процессоров в одной компьютерной системе. Термин также относится к способности системы поддержать больше чем один процессор и/или способность распределить задачи между ними. Существует много вариантов данного понятия, и определение многопроцессорности может меняться в зависимости от контекста, главным образом в зависимости от того, как определены процессоры (много ядер в одном кристалле, множество чипов в одном корпусе, множество корпусов в одном системном модуле, и т. д.).

Присоединённые устройства

Архитектура NVIDIA CUDA

Сама парадигма приложений использующих графические ускорители появилась в 2003 году. В основном этим занимались энтузиасты. Однако, компания NVidia рассмотрела в этом потенциал. Потенциал так же увидели ряд нефтяных компаний, перед которыми стояли проблемы обработки больших объемов данных и масштабных расчетов. Эти компании так же могли инвестировать средства в разработку.

После создания первых карт для вычислений, встал вопрос создания удобного языка программирования для GPU. Добавив расширение для языка программирования С и С++. Программирование для графических процессоров вышло на новый уровень. Резко возросло количество научных публикаций. Применение GPU стало распространятся на многие сферы. Именно момент внесения расширения для языков программирования, можно считать рождением архитектуры CUDA для GPU.

Компания NVidia так же развивает свои библиотеки для удобства разработки под эти устройства, в каждую новая редакцию библиотек вносятся новые алгоритмы и более оптимальные решения старых. Например, библиотека cuBLAS. cuBLAS это реализация библиотеки BLAS(Basic Linear Algebra Subprograms- Базовые Подпрограммы Линейной Алгебры) стандарт де-факто интерфейса программирования приложений для создания библиотек, выполняющих основные операции линейной алгебры, такие как умножение векторов и матриц.

Кроме того, центральные процессоры используют SIMD (одна инструкция выполняется над многочисленными данными) блоки для векторных вычислений, а видеочипы применяют SIMT (одна инструкция и несколько потоков) для скалярной обработки потоков. SIMT не требует, чтобы разработчик преобразовывал данные в векторы, и допускает произвольные ветвления в потоках.

Так же стоит отметить при использовании GPU разработчику доступно несколько видов памяти: регистры, локальная, глобальная, разделяемая, константная и текстурная память. Каждая из этих типов памяти имеет определённое назначение, которое обуславливается её техническими параметрами (скорость работы, уровень доступа на чтение и запись).

Количество ядер в последних версих графических процессоров может достигать 2496.

Операция транспонирование матрицы выглядит следующим образом,

$$A_{ij}^T =A_{ji}$$

а функция транспонированная на CUDA.

__global__ void transposeNaive(float *odata, const float *idata)
{
  int x = blockIdx.x * TILE_DIM + threadIdx.x;
  int y = blockIdx.y * TILE_DIM + threadIdx.y;
  int width = gridDim.x * TILE_DIM;

  for (int j = 0; j < TILE_DIM; j+= BLOCK_ROWS)
    odata[x*width + (y+j)] = idata[(y+j)*width + x];
}

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

Ниже приведена архитектура устройства:

Архитектура Intel MIC (Many Integrated Core Architecture)

В основе архитектуры Intel MIC (большое количечство ядер в одной микросхеме) лежит классическая архитектура x86, на ускорителе исполняется ОС Linux. Для программирования MIC предполагается использовать OpenMP, OpenCL, Intel Cilk Plus, специализированные компиляторы Intel Fortran, Intel C++. Также предоставляются математические библиотеки.

Сопроцессоры Intel Xeon Phi(архитектура Intel MIC) могут иметь до 61 ядра, 244 потоков и обеспечивать производительность до 1,2 терафлопс. Они представлены во множестве конфигураций для удовлетворения различных требований к аппаратным и программным средствам, рабочим нагрузкам, производительности и эффективности.

Вы наверняка заметили, что у Intel Xeon Phi гораздо меньше ядер, чем у обычного графического процессора. Но нельзя сравнивать ядро MIC с CUDA в соотношении один к одному. Одно ядро Intel Xeon Phi – это четырёхпоточный модуль с 512-бит SIMD. Для верного сопоставления не стоит обращать внимание на маркетинговое определение понятия "ядра".

Нужно отметить тот факт, что написать приложение для архитектуры Intel MIC, проще чем на CUDA. Однако, нужно приложить усилия при написания программ, что бы достигнуть максимальной производительности.

$$A_{ij}^T =A_{ji}$$ Операция транспонирование матрицы

 //FTYPE = float
#pragma omp parallel for
for (int i = 0; i < n; i++)
     for (int j = 0; j<i; j++) 
     {
        const FTYPE c = A[i*n + j];
        A[i*n + j] = A[j*n + i];
        A[j*n + i] = c;
     }

будет приведена к следующему виду

 //FTYPE = float 
void Transpose(FTYPE* const A, const int n, const int* const plan)
{

  // nEven is a multiple of TILE
  const int nEven = n - n%TILE;
  const int wTiles = nEven / TILE;                  // Complete tiles in each dimens.
  const int nTilesParallel = wTiles*(wTiles - 1)/2; // # блоков матриц

#pragma omp parallel
  { 
    #pragma omp for schedule(guided)
    for (int k = 0; k < nTilesParallel; k++)
    {
      //Для матриц больших размерностей большая часть работы будет в данном цикле

      const int ii = plan[2*k + 0];
      const int jj = plan[2*k + 1];

      // Транспонирование блоков
      for (int j = jj; j < jj+TILE; j++) 
      { 
        #pragma simd
        for (int i = ii; i < ii+TILE; i++)
        {
          const FTYPE c = A[i*n + j];
          A[i*n + j] = A[j*n + i];
          A[j*n + i] = c;

        }
      }
    }

    //Транспонирование блоков вдоль главной диагонали
    #pragma omp for schedule(static)
    for (int ii = 0; ii < nEven; ii += TILE) 
    {
      const int jj = ii;

      for (int j = jj; j < jj+TILE; j++) 
        #pragma simd
        for (int i = ii; i < j; i++) 
        {
          const FTYPE c = A[i*n + j];
          A[i*n + j] = A[j*n + i];
          A[j*n + i] = c;
        }
    }

    // Транспонирование "Шубы"  правой и нижней части матрицы.
    #pragma omp for schedule(static)
    for (int j = 0; j < nEven; j++) 
    {
      for (int i = nEven; i < n; i++)
      {
        const FTYPE c = A[i*n + j];
        A[i*n + j] = A[j*n + i];
        A[j*n + i] = c;
      }
    }

  }

  // Транспонирование нижнего правого угла
  for (int j = nEven; j < n; j++)
  {
    for (int i = nEven; i < j; i++) 
    {
      const FTYPE c = A[i*n + j];
      A[i*n + j] = A[j*n + i];
      A[j*n + i] = c;
    }
  }

}

Второй вариант работает более чем в 2 раза быстрее на сопроцессоре Intel Xeon Phi (архитектура Intel MIC). Данная операция была исследована Андреем Владимировым из Colfax Reseach (США)

Ниже приведена архитектура устройства:

Двунаправленная кольцевая шина обеспечивает передачу данных между компонентами сопроцессора. Восемь контроллеров памяти обслуживают 16 каналов GDDR5. Каждый контроллер памяти включает два независимых канала доступа к памяти, при этом все контроллеры памяти сопроцессора действуют независимо друг от друга.