Низкая эффективность процессора при почти одинаковых ядрах CUDA

Я создал три синтетических ядра CUDA, которые почти все выполняют только арифметические операции. Все три ядра одинаковы, за исключением того, что каждое из них выполняет различное количество операций. Ядро № 1 выполняет 8 операций, Ядро № 2 выполняет 16 операций, а Ядро № 3 выполняет 32. Вот реализации ядра CUDA для всех трех.

Ядро № 1:

#ifndef kernelWGSXMAPIXLLXOPS8_H_
#define kernelWGSXMAPIXLLXOPS8_H_

__global__ void WGSXMAPIXLLXOPS8 (const float *GIn, float *GOut, const float M, const float N, const float P) {

        int gid = blockIdx.x * blockDim.x + threadIdx.x;

        float MF = (float) M;
  float NF = (float) N;
  float PF = (float) P;

  for (int lcdd = 0; lcdd < 1; lcdd++) {
    float temp1 = 1.0;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    ... // 8 FMA operations
    temp1 = temp1 * MF + temp1;

    GOut[gid] = temp1;
  }

}

void WGSXMAPIXLLXOPS8_wrapper (const float *GIn, float *GOut,
                               const float M, const float N, const float P,
                               int numBlocks, int threadPerBlock) {
        WGSXMAPIXLLXOPS8<<<numBlocks, threadPerBlock>>> (GIn, GOut, M, N, P); 
}


#endif     

Ядро № 2:

#ifndef kernelWGSXMAPIXLLXOPS16_H_
#define kernelWGSXMAPIXLLXOPS16_H_

__global__ void WGSXMAPIXLLXOPS16 (const float *GIn, float *GOut, const float M, const float N, const float P) {

        int gid = blockIdx.x * blockDim.x + threadIdx.x;

        float MF = (float) M;
  float NF = (float) N;
  float PF = (float) P;

  for (int lcdd = 0; lcdd < 1; lcdd++) {
    float temp1 = 1.0;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    ... // 16 FMA operations
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;

    GOut[gid] = temp1;
  }

}

void WGSXMAPIXLLXOPS16_wrapper (const float *GIn, float *GOut,
                               const float M, const float N, const float P,
                               int numBlocks, int threadPerBlock) {
        WGSXMAPIXLLXOPS16<<<numBlocks, threadPerBlock>>> (GIn, GOut, M, N, P); 
}

#endif

Ядро № 3:

#ifndef kernelWGSXMAPIXLLXOPS32_H_
#define kernelWGSXMAPIXLLXOPS32_H_

__global__ void WGSXMAPIXLLXOPS32 (const float *GIn, float *GOut, const float M, const float N, const float P) {

        int gid = blockIdx.x * blockDim.x + threadIdx.x;

        float MF = (float) M;
  float NF = (float) N;
  float PF = (float) P;

  for (int lcdd = 0; lcdd < 1; lcdd++) {
    float temp1 = 1.0;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    ... // 32 FMA operations
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;

    GOut[gid] = temp1;
  }

}

void WGSXMAPIXLLXOPS32_wrapper (const float *GIn, float *GOut,
                               const float M, const float N, const float P,
                               int numBlocks, int threadPerBlock) {
        WGSXMAPIXLLXOPS32<<<numBlocks, threadPerBlock>>> (GIn, GOut, M, N, P); 
}

#endif

Общее число потоков было установлено равным 16384, а размер блока - 256. Я рассчитал общее количество GFlops каждого из этих ядер и равняется 20,44, 56,53 и 110,12 GFlops. Я пытался придумать объяснение, но мне ничего не приходит в голову. Поэтому я попытался использовать nvprof и отслеживал все показатели. Все метрики почти равны. Вот некоторые метрики, которые выглядят важными для меня (я также включил результаты для ядра 1–3):

sm_efficiency_instance:   14.99, 16.78, 19.82 %
ipc_instance:             0.57 , 0.93 , 1.53   
inst_replay_overhead:     0.399, 0.268, 0.165
dram_write_throughput:    18.08, 17.72, 16.9 GB/s
issued_ipc:               0.99 , 1.18 , 1.52
issue_slot_utilization:   19.48, 24.64, 33.76 %
stall_exec_dependency:    21.84, 26.38, 42.95 %

Как понятно, оба они имеют одинаковый dram_write_throughput, так как все записывают одинаковый объем данных в DRAM, а общее количество потоков одинаково. Что я не понимаю, так это sm_efficiency. Все мои ядра занимаются арифметикой (одинаково), поэтому их sm_efficiency - не то же самое. Кроме того, почему наличие большего количества арифметики в одном и том же ядре повышает эффективность? Насколько я понимаю, у всех них должна быть одна и та же проблема с поиском перекосов для поиска на SM.

Может кто-нибудь помочь мне понять разницу GFlops, используя приведенные ниже метрики?

1 ответ

Решение

Основная проблема в том, что вы не "насыщали" GPU работой. С запуском ядра связаны различные накладные расходы. Если количество времени, которое ядро ​​тратит на вычисления, невелико по сравнению с этими издержками, тогда ваши вычисления будут искажены накладными расходами.

T = время накладных расходов (OT) + время расчета (CT)

Флопс / с = Флопс / Т = Флопс (OT + CT)

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

Вот полный тестовый пример, с несколькими запущенными случаями, CUDA 9.1, Tesla P100 PCIE:

$ cat t79.cu
#ifndef SLEN
#define SLEN (8)
#endif
#ifndef NTPB
#define NTPB (256)
#endif
#ifndef BLKS
#define BLKS (16384/NTPB)
#endif
const size_t blks = BLKS;
const size_t ntpb = NTPB;
typedef float Ftype;
#include <iostream>
template <int LEN>
__global__ void WGSXMAPIXLLXOPS (Ftype *GOut, const Ftype M) {

        int gid = blockIdx.x * blockDim.x + threadIdx.x;

        Ftype MF = (Ftype) M;

  for (int lcdd = 0; lcdd < 1; lcdd++) {
    float temp1 = 1.0;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    temp1 = temp1 * MF + temp1;
    if (LEN > 8){
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;}
    if (LEN > 16){
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;}
    if (LEN > 32){
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;}
    if (LEN > 64){
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;}
    if (LEN > 128){
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;}
    if (LEN > 256){
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;
      temp1 = temp1 * MF + temp1;}


#ifdef NO_WRITE
      if (temp1 == -10.0)
#endif
        GOut[gid] = temp1;
  }

}


int main(){

  float et;
  Ftype *GOut;
  const Ftype M = 1.0;
  cudaMalloc(&GOut, blks*ntpb*sizeof(Ftype));
  cudaEvent_t start, stop;
  cudaEventCreate(&start); cudaEventCreate(&stop);
  WGSXMAPIXLLXOPS<SLEN><<<blks, ntpb>>> (GOut, M);
  cudaDeviceSynchronize();
  cudaEventRecord(start);
  WGSXMAPIXLLXOPS<SLEN><<<blks, ntpb>>> (GOut, M);
  cudaEventRecord(stop);
  cudaEventSynchronize(stop);
  cudaEventElapsedTime(&et, start, stop);
  unsigned long long flpcnt = SLEN*2*blks*ntpb;
  float Kflops_s = flpcnt/et;
  std::cout << "MFlops per sec: " << Kflops_s/1000 << " kernel time: " << et << "ms" << std::endl;
  cudaDeviceSynchronize();
}
$ nvcc -arch=sm_60 -o t79 t79.cu
$ ./t79
MFlops per sec: 14371.9 kernel time: 0.01824ms
$ nvprof ./t79
==14676== NVPROF is profiling process 14676, command: ./t79
MFlops per sec: 10101.1 kernel time: 0.025952ms
==14676== Profiling application: ./t79
==14676== Profiling result:
            Type  Time(%)      Time     Calls       Avg       Min       Max  Name
 GPU activities:  100.00%  3.2320us         2  1.6160us  1.2480us  1.9840us  void WGSXMAPIXLLXOPS<int=8>(float*, float)
      API calls:   98.31%  389.62ms         1  389.62ms  389.62ms  389.62ms  cudaMalloc
                    1.10%  4.3574ms       376  11.588us     357ns  465.31us  cuDeviceGetAttribute
                    0.42%  1.6829ms         4  420.73us  272.19us  642.45us  cuDeviceTotalMem
                    0.12%  487.27us         4  121.82us  90.094us  164.09us  cuDeviceGetName
                    0.02%  80.363us         2  40.181us  15.789us  64.574us  cudaLaunch
                    0.00%  17.118us         2  8.5590us  8.1400us  8.9780us  cudaDeviceSynchronize
                    0.00%  13.118us         2  6.5590us  5.4290us  7.6890us  cudaEventRecord
                    0.00%  10.603us         2  5.3010us  1.2440us  9.3590us  cudaEventCreate
                    0.00%  8.5080us         8  1.0630us     460ns  1.7500us  cuDeviceGet
                    0.00%  8.4590us         1  8.4590us  8.4590us  8.4590us  cudaEventElapsedTime
                    0.00%  7.1350us         1  7.1350us  7.1350us  7.1350us  cudaEventSynchronize
                    0.00%  6.8430us         4  1.7100us     180ns  5.9720us  cudaSetupArgument
                    0.00%  4.7800us         3  1.5930us     437ns  2.8480us  cuDeviceGetCount
                    0.00%  2.3490us         2  1.1740us     361ns  1.9880us  cudaConfigureCall
$ nvcc -arch=sm_60 -o t79 t79.cu -DSLEN=512 -DBLKS=32768 -DNTPB=1024
$ ./t79
MFlops per sec: 8.08072e+06 kernel time: 4.25206ms
$
$ nvprof --metrics  sm_efficiency_instance,ipc_instance,issued_ipc,issue_slot_utilization,stall_exec_dependency    ./t79
==15447== NVPROF is profiling process 15447, command: ./t79
==15447== Some kernel(s) will be replayed on device 0 in order to collect all events/metrics.
Replaying kernel "void WGSXMAPIXLLXOPS<int=512>(float*, float)" (done)
Replaying kernel "void WGSXMAPIXLLXOPS<int=512>(float*, float)" (done)
MFlops per sec: 193432 kernel time: 177.632ms
==15447== Profiling application: ./t79
==15447== Profiling result:
==15447== Metric result:
Invocations                               Metric Name                           Metric Description         Min         Max         Avg
Device "Tesla P100-PCIE-16GB (0)"
    Kernel: void WGSXMAPIXLLXOPS<int=512>(float*, float)
          2                                issued_ipc                                   Issued IPC    1.972106    1.972388    1.972247
          2                    issue_slot_utilization                       Issue Slot Utilization      98.23%      98.24%      98.24%
          2                     stall_exec_dependency   Issue Stall Reasons (Execution Dependency)      16.35%      16.36%      16.36%
          2                                       ipc                                 Executed IPC    1.971976    1.972254    1.972115
          2                             sm_efficiency                      Multiprocessor Activity      99.78%      99.78%      99.78%
$

При первом запуске с числами, которые совпадают с вашими (16384 потоков, 256 потоков на блок, 8 инструкций FFMA), продолжительность ядра составляет ~17us. Однако, когда мы запускаем этот случай в профилировщике, мы видим, что фактическое выполнение ядра составляет всего около 1,5 мкс, а остаток - это различные виды накладных расходов, включая задержку запуска ядра, а также задержку использования cudaEvent система для синхронизации. Так что это отбрасывает цифры далеко.

С другой стороны, когда мы запускаем большое количество блоков и потоков на блок и работаем на поток, мы получаем число, которое составляет 80% от максимальной производительности P100.

Большинство ваших показателей увеличиваются (улучшаются) по мере того, как вы переходите от ядра 1 к 3 (за исключением пропускной способности драма, что разумно. По мере увеличения времени ядра для того же объема записанных данных средняя пропускная способность драма снижается). Это согласуется с предоставлением графическому процессору больше работы, так что он может скрывать различные виды задержки и амортизировать накладные расходы при большом количестве работы.

Давайте посмотрим на некоторые из этих метрик для финального запуска /"большого" ядра выше:

2                 issued_ipc                                   Issued IPC    1.972106    1.972388    1.972247
2     issue_slot_utilization                       Issue Slot Utilization      98.23%      98.24%      98.24%
2      stall_exec_dependency   Issue Stall Reasons (Execution Dependency)      16.35%      16.36%      16.36%
2                        ipc                                 Executed IPC    1.971976    1.972254    1.972115
2              sm_efficiency                      Multiprocessor Activity      99.78%      99.78%      99.78%

IPC составляет около 2 за такт, что выше, чем у вашего ядра 3. Обратите внимание, что IPC 2 является разумной верхней границей: sm_60 SM имеет 64 устройства с одинарной точностью, что достаточно для планирования 2 команд FFMA за такт.

Эффективность SM и issue_slot_utilization являются похожими показателями. Это означает, что примерно в 98% случаев SM может выдавать одну или несколько инструкций в любой заданный тактовый цикл.

Задержка (зависимость exec) отвечает на вопрос: "Во всех фактических ситуациях остановки какой процент был вызван зависимостью выполнения?". У вашего ядра есть зависимость выполнения между каждой строкой исходного кода - так как каждая зависит от результатов предыдущей строки. Это означает, что на уровне сборки каждая инструкция FFMA будет зависеть от результатов предыдущей, поэтому ее нельзя выполнить до завершения предыдущей.

Если бы SM был подписан на доступную работу, то зависимость stall exec возросла бы, потому что препятствием для выдачи дополнительной работы была бы зависимость exec. Число 16% здесь означает, что примерно в 5/6 случаев, когда существует сценарий останова, это не связано с зависимостью exec. Другими словами, даже несмотря на то, что у нас в ядре много зависимостей от выполнения, большую часть времени, когда происходило зависание, это было не потому, что GPU хотел бы перейти к следующей строке кода для выдачи - это было для некоторых другая причина.

Резюме:

Кажется, что есть как минимум 2 проблемы, обе из которых связаны с различными типами задержки:

  1. При очень малых размерах ядра (например, общее количество потоков 16384) время выполнения ядра короткое, поэтому измерение затуманено, например, задержкой запуска ядра и, возможно, задержкой измерения.
  2. Размеры ядра, будучи очень маленькими, не насыщают GPU настолько параллельной работой, сколько может быть доставлено, и таким образом, такими как IPC и sm_efficiency ниже, чем они должны быть, и приводят к задержкам: зависимость exec относительно высока.

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

Это согласуется с логикой оптимизации на основе анализа (слайд 46 и далее)

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

Другие вопросы по тегам