проблем при четене от __глобална памет след atom_inc в OpenCL

OpenCL няма глобална бариера, която да спре всички нишки, така че се опитвам да създам решение със следния код:

void barrier(__global uint* scratch) {
  uint nThreads = get_global_size(0);
  atom_inc(scratch);
  /* this loop never terminates */
  while(scratch[0] < nThreads) {
    continue;
  }
}

Идеята е, че всяка нишка се зацикля, докато всички те увеличат тази част от паметта.

Въпреки това стойността, прочетена от нулата [0], никога не се променя за нишките, след като бъде прочетена, и зацикля завинаги. Знам, че се увеличава, защото това е правилната стойност, когато я прочета обратно на хоста.

Локално ли се кешира глобалната памет? Какво става тук?


person Steve Blackwell    schedule 29.09.2010    source източник
comment
Каква платформа/драйвери/и т.н. тествате ли това на?   -  person runexe    schedule 30.09.2010
comment
защо искаш да направиш това? Синхронизирането между работни групи не може да работи, защото те може да не работят едновременно. И дори това да работи: поне за gpus (opencl само за процесор няма смисъл...) ще бъде невероятно бавно, тъй като трябва да сериализира всички тези достъпи до глобалната памет. Така че дори ако използвате само 10 000 нишки (които биха се изпълнили напълно едновременно на GPU от среден до висок клас) и atomics работят върху чип (така че само Ферми), такава бариера пак ще струва ›1000 000 цикъла (всяка нишка има няколко цикъла латентност както за четене, така и за писане , една нишка наведнъж)   -  person Grizzly    schedule 01.10.2010
comment
Стандартният начин за използване на глобална синхронизация в opencl е стартиране на ново ядро, което трябва да е много по-бързо за разумен брой нишки (не съм сигурен за nvidia в момента, но amd поставя 225µs като латентност на kernellauch, докато глобална бариера като тази би струвала няколко мс.   -  person Grizzly    schedule 01.10.2010
comment
Това, че работните групи не работят непременно едновременно, е ключовият факт, който не знаех, и затова го публикувах в отговора по-долу. Бях по-малко загрижен за производителността, тъй като трябва да правя това само веднъж на изпълнение.   -  person Steve Blackwell    schedule 01.10.2010
comment
Ако производителността няма значение, има още по-малка причина просто да не разделяте функционалността на няколко ядра и да използвате стартирането на ново ядро ​​като точка за синхронизация (разбира се, тъй като по-големите ядра означават повече регистри/нишки с по-малки ядра дори може да подобри производителността ). Освен това все още смятам, че има съображения за производителност в дългосрочен план, защото защо иначе да използвам opencl. Но просто исках да изясня защо това е лоша идея, дори и да работи, в края на краищата други може да опитат с по-малко работни групи ;).   -  person Grizzly    schedule 03.10.2010


Отговори (1)


Открих проблема: редът, в който се изпълняват работните групи, е дефиниран от изпълнението. Това означава, че някои теми може да започнат едва след като други са приключили.

В кода, който дадох, работните групи, които са стартирани първи, ще се зациклят завинаги, чакайки останалите да ударят „бариерата“. И работните групи, които биха започнали по-късно, никога няма да започнат, защото чакат първите да завършат.

Ако внедряването (аз съм на Radeon 5750, използвайки Stream SDK 2.2) изпълнява всички работни групи едновременно, тогава вероятно няма да е проблем. Но това не е така за моята настройка.

person Steve Blackwell    schedule 30.09.2010