Nano Hash - криптовалюты, майнинг, программирование

memset в CUBLAS всегда запускается в потоке по умолчанию

Я заметил, что при вызове функции cublasSgemm для каждого вызова gemm с хоста происходит 3 вызова ядра: memset, scal_kernel и само ядро ​​gemm (например, sgemm_large). Это происходит, даже если я использую константы альфа/бета, выделенные в памяти устройства. Хотя накладные расходы на memset и scal_kernel относительно невелики, проблема заключается в том, что memset всегда запускается в потоке по умолчанию, что вызывает ненужную синхронизацию.

Код:

__constant__ __device__ float alpha = 1;
__constant__ __device__ float beta = 1;

int main()
{
    // ... memory allocation skipped ...
    float* px = thrust::raw_pointer_cast(x.data());
    float* py = thrust::raw_pointer_cast(y.data());
    float* pmat = thrust::raw_pointer_cast(mat.data());
    for (int iter = 0; iter < 3; ++iter)
    {
        cbstatus = cublasSgemm(cbh, CUBLAS_OP_N, CUBLAS_OP_N, crow, ccol, cshared, &alpha, px, crow, py, cshared, &beta, pmat, crow);
        assert(0 == cbstatus);
    }
}

Вот что я вижу в профилировщике:

memset в профайлере

Вопрос: есть ли способ избежать memset или заставить его работать в потоке, назначенном дескриптору CUBLAS? Одна идея состоит в том, чтобы использовать DP и запустить версию функции gemm для устройства, но это будет работать только на CC 3.0 и выше.

26.02.2014

  • Как вы можете сказать, что memset всегда запускается в потоке по умолчанию? Я не вижу cublasSetStream в вашем коде до вызова cublasSgemm. 27.02.2014

Ответы:


1

В CUBLAS5.5 была ошибка, из-за которой вместо cudaMemsetAsync в специализированном пути, где k >> m,n, использовалось cudaMemset.

Это исправлено в CUBLAS6.0 RC. И вы можете получить к нему доступ, если вы являетесь зарегистрированным разработчиком.

Кстати, мне интересно, почему вы используете __constant__ __device__ для альфы, беты. Вы используете pointerMode = DEVICE?

Если нет, вы можете просто использовать альфа, бета на хосте.

26.02.2014
  • Спасибо, это еще одна причина для меня перейти на 6.0. 27.02.2014

  • 2

    Попробуйте код ниже. Код задуман так, чтобы иметь только вызов cublasSgemm, не считая неизбежных выделений памяти и копий. Вы увидите, что

    1. У вас запущено только одно ядро ​​(gemm_kernel1x1_core);
    2. Два вызова cublasSgemm отлично выполняются в двух разных потоках.

    На картинке показана временная шкала Visual Profiler.

    Моя система: GeForce 540M, Windows 7, CUDA 5.5.

    введите здесь описание изображения

    #include <conio.h>
    #include <stdio.h>
    #include <assert.h>
    
    #include <cublas_v2.h> 
    
    /********************/
    /* CUDA ERROR CHECK */
    /********************/
    #define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
    inline void gpuAssert(cudaError_t code, char *file, int line, bool abort=true)
    {
        if (code != cudaSuccess) 
        {
            fprintf(stderr,"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
            if (abort) { getchar(); exit(code); }
        }
    }
    
    /**********************/
    /* cuBLAS ERROR CHECK */
    /**********************/
    #ifndef cublasSafeCall
    #define cublasSafeCall(err)     __cublasSafeCall(err, __FILE__, __LINE__)
    #endif
    
    inline void __cublasSafeCall(cublasStatus_t err, const char *file, const int line)
    {
        if( CUBLAS_STATUS_SUCCESS != err) {
            fprintf(stderr, "CUBLAS error in file '%s', line %d\n \nerror %d \nterminating!\n",__FILE__, __LINE__,err); 
            getch(); cudaDeviceReset(); assert(0); 
        }
    }
    
    /********/
    /* MAIN */
    /********/
    int main()
    {
        int N = 5;
    
        float *A1, *A2, *B1, *B2, *C1, *C2;
        float *d_A1, *d_A2, *d_B1, *d_B2, *d_C1, *d_C2;
    
        A1 = (float*)malloc(N*N*sizeof(float));
        B1 = (float*)malloc(N*N*sizeof(float));
        C1 = (float*)malloc(N*N*sizeof(float));
    
        A2 = (float*)malloc(N*N*sizeof(float));
        B2 = (float*)malloc(N*N*sizeof(float));
        C2 = (float*)malloc(N*N*sizeof(float));
    
        gpuErrchk(cudaMalloc((void**)&d_A1,N*N*sizeof(float)));
        gpuErrchk(cudaMalloc((void**)&d_B1,N*N*sizeof(float)));
        gpuErrchk(cudaMalloc((void**)&d_C1,N*N*sizeof(float)));
        gpuErrchk(cudaMalloc((void**)&d_A2,N*N*sizeof(float)));
        gpuErrchk(cudaMalloc((void**)&d_B2,N*N*sizeof(float)));
        gpuErrchk(cudaMalloc((void**)&d_C2,N*N*sizeof(float)));
    
        for (int i=0; i<N*N; i++) {
            A1[i] = ((float)rand()/(float)RAND_MAX);
            A2[i] = ((float)rand()/(float)RAND_MAX);
            B1[i] = ((float)rand()/(float)RAND_MAX);
            B2[i] = ((float)rand()/(float)RAND_MAX);
        }
        gpuErrchk(cudaMemcpy(d_A1, A1, N*N*sizeof(float), cudaMemcpyHostToDevice));
        gpuErrchk(cudaMemcpy(d_B1, B1, N*N*sizeof(float), cudaMemcpyHostToDevice));
        gpuErrchk(cudaMemcpy(d_A2, A2, N*N*sizeof(float), cudaMemcpyHostToDevice));
        gpuErrchk(cudaMemcpy(d_B2, B2, N*N*sizeof(float), cudaMemcpyHostToDevice));
    
        cublasHandle_t handle;
        cublasSafeCall(cublasCreate(&handle));
    
        cudaStream_t stream1, stream2;
        gpuErrchk(cudaStreamCreate(&stream1));
        gpuErrchk(cudaStreamCreate(&stream2));
    
        float alpha = 1.f;
        float beta = 1.f;
    
        cublasSafeCall(cublasSetStream(handle,stream1));
        cublasSafeCall(cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, N, N, &alpha, d_A1, N, d_B1, N, &beta, d_C1, N));
        cublasSafeCall(cublasSetStream(handle,stream2));
        cublasSafeCall(cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, N, N, N, &alpha, d_A2, N, d_B2, N, &beta, d_C2, N));
    
        gpuErrchk(cudaDeviceReset());
    
        return 0;
    
     }
    
    26.02.2014
  • Как указал Филипп, проблема заключается в ошибке в CUBLAS 5.5, которая возникает, когда общее измерение намного больше, чем размер строки/столбца. Если вы установите k равным 10000, а m,n равным 1000, вы увидите проблему, которую я описал. Прошу прощения за не полную информацию в своем вопросе. 27.02.2014
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..