CUDA: распределение общей памяти с перекрывающимися границами - PullRequest
3 голосов
/ 06 апреля 2011

Есть ли простой способ (google еще не доставил ...) выделить области разделяемой памяти для каждого блока из одного входного массива так, чтобы их можно было перекрывать?

Простой пример - поиск строки; Увидел, что я хочу нарезать входной текст, чтобы каждый поток в каждом блоке искал шаблон, начинающийся с text [thread_id], но хотел, чтобы данные, назначенные каждому блоку, перекрывались по длине шаблона, поэтому совпадающие случаи, попадающие через границу, все еще найден.

Т.е. общий объем памяти, выделенный общей памяти для каждого блока, равен

(blocksize+patternlength)*sizeof(char)

Я, вероятно, упускаю что-то простое и сейчас изучаю руководство CUDA, но буду признателен за некоторые рекомендации.

ОБНОВЛЕНИЕ: я подозреваю, что некоторые люди неправильно поняли мой вопрос (или я неправильно объяснил его).

Скажем, у меня есть набор данных QWERTYUIOP, и я хочу найти совпадение из 3 символов, и я делю набор данных (произвольно) на 4 для каждого блока потока; QWER TYUI OPxx

Это достаточно просто сделать, но алгоритм не работает, если совпадение из 3 символов действительно ищет IOP.

В данном случае я хочу, чтобы каждый блок имелся в общей памяти:

QWERTY TYUIOP OPxxxx

то есть каждому блоку назначаются символы размера блока + patternlength-1, поэтому не возникает проблем с границами памяти

Надеюсь, это объясняет лучше.

Поскольку @jmilloy является постоянным ...: P

//VERSION 1: Simple
__global__ void gpuSearchSimple(char *T, int lenT, char *P, int lenP, int *pFound)
{
  int startIndex = blockDim.x*blockIdx.x + threadIdx.x;
    int fMatch = 1;
    for (int i=0; i < lenP; i++)
    {
      if (T[startIndex+i] != P[i]) fMatch = 0;
    }
    if (fMatch) atomicMin(pFound, startIndex);
}
//VERSION 2: Texture
__global__ void gpuSearchTexture(int lenT, int lenP, int *pFound)
{
  int startIndex = blockDim.x*blockIdx.x + threadIdx.x;
    int fMatch = 1;
    for (int i=0; i < lenP; i++)
    {
      if (tex1Dfetch(texT,startIndex+i) != tex1Dfetch(texP,i)) fMatch = 0;
    }
    if (fMatch) atomicMin(pFound, startIndex);
}
//Version 3: Shared
__global__ void gpuSearchTexSha(int lenT, int lenP, int *pFound)
{
  extern __shared__ char shaP[];
  for (int i=0;threadIdx.x+i<lenP; i+=blockDim.x){
    shaP[threadIdx.x+i]= tex1Dfetch(texP,threadIdx.x+i);
  }
  __syncthreads();

  //At this point shaP is populated with the pattern
  int startIndex = blockDim.x*blockIdx.x + threadIdx.x;
    // only continue if an earlier instance hasn't already been found
    int fMatch = 1;
    for (int i=0; i < lenP; i++)
    {
      if (tex1Dfetch(texT,startIndex+i) != shaP[i]) fMatch = 0;
    }
    if (fMatch) atomicMin(pFound, startIndex);
}

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

Ответы [ 3 ]

2 голосов
/ 06 апреля 2011

Я не уверен, что этот вопрос имеет столько смысла.Вы можете динамически изменять размер разделяемой памяти во время выполнения, например:

__global__ void kernel()
{
    extern __shared__ int buffer[];
    ....
}

kernel<<< gridsize, blocksize, buffersize >>>();

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

1 голос
/ 07 апреля 2011

Нет. Общая память распределяется между потоками в блоке и доступна ТОЛЬКО для блока, которому она назначена. У вас не может быть общей памяти, доступной для двух разных блоков.

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

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

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


редактировать 2
после того, как ответ был помечен как правильный

__global__ void gpuSearchTexSha(int lenT, int lenP, int *pFound)
{
    extern __shared__ char* shared;

    char* shaP = &shared[0];
    char* shaT = &shared[lenP];

    //copy pattern into shaP in parallel
    if(threadIdx.x < lenP)
        shaP[threadIdx.x] = tex1Dfetch(texP,threadIdx.x);

    //determine texT start and length for this block
    blockStartIndex = blockIdx.x * gridDim.x/lenT;
    lenS = gridDim.x/lenT + lenP - 1;

    //copy text into shaT in parallel
    shaT[threadIdx.x] = tex1Dfetch(texT,blockStartIndex + threadIdx.x);
    if(threadIdx.x < lenP)
        shaP[blockDim.x + threadIdx.x] = text1Dfetch(texT,blockStartIndex + blockDim.x + threadIdx.x)

    __syncthreads();

    //We have one pattern in shaP for each thread in the block
    //We have the necessary portion of the text (with overlaps) in shaT

    int fMatch = 1;
    for (int i=0; i < lenP; i++)
    {
        if (shaT[threadIdx.x+i] != shaP[i]) fMatch = 0;
    }
    if (fMatch) atomicMin(pFound, blockStartIndex + threadIdx.x);
}

ключевые ноты:

  • нам нужна только одна копия шаблона в общей памяти на блок - все они могут использовать его
  • общей памяти, необходимой для каждого блока, составляет lenP + lenS (где lenS - ваш blocksize + patternlength)
  • ядро ​​предполагает, что gridDim.x * blockDim.x = lenT (аналогично версии 1)
  • мы можем копировать в разделяемую память параллельно (нет необходимости в циклах for, если у вас достаточно потоков)
1 голос
/ 06 апреля 2011

Перекрывающаяся разделяемая память не годится, поток должен будет синхронизироваться каждый раз, когда он хочет получить доступ к одному и тому же адресу в разделяемой памяти (хотя в архитектуре> = 2.0 это было смягчено).

Самая простая идеямне приходит в голову дублировать часть текста, которую вы хотите перекрыть.

Вместо чтения из глобальной памяти точными кусками:

AAAA BBBB CCCC DDDDEEEE

Чтение с перекрытием:

AAAA BBBB CCCC CCCC DDDD EEEEE

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