Каталог
ZV
ездный б-р, 19
+7 (495) 974-3333 +7 (495) 974-3333 Выбрать город: Москва
Подождите...
Получить токен
Соединиться
X
Сюда
Туда
x
Не выбрано товаров для сравнения
x
Корзина пуста
Итого: 
Оформить заказ
Сохранить заказ
Открыть корзину
Калькуляция
Очистить корзину
x
Главная
Магазины
Каталог
Мои заказы
Корзина
Магазины Доставка по РФ
Город
Область
Ваш город - ?
От выбранного города зависят цены, наличие товара и
способы доставки

Четверг, 1 марта 2007 00:00

NVIDIA открывает CUDA

короткая ссылка на новость:

Новые концепции для CUDA. Часть 1



   Кэш параллельных данных (parallel data cache - PDC), также известный как "совместно используемая память", является одним из основных элементов архитектуры G80, который позволяет работать с более эффективными примитивами синхронизации локальных данных. Похоже, что это основной элемент архитектуры NVIDIA для ускорения работы геометрических шейдеров DirectX 10.

   Перед тем как идти дальше, посмотрите на архитектуру NVIDIA G8x и CUDA. Эта блок-схема очень похожа на одну из диаграмм из патентов NVIDIA, зарегистрированных в 2003 году.

Процессоры, мультипроцессоры и ещё процессоры

   В G80 варианте этой архитектуры на каждый мультипроцессор приходится 8 процессоров (ALU-арифметико-логических устройств) и 16 мультипроцессоров на чип. Кроме того, как видно на блок-схеме, у каждого процессора есть минимум один регистр.

   Опять же, на блок-схеме можно заметить, что на каждый мультипроцессор приходится 1пул общей памяти (shared memory). А общаться между собой разные мультипроцессоры могут только через память устройства (device memory). К тому же, нет никаких естественных примитивов, чтобы упростить эту ситуацию. Вот почему мы называем возможности синхронизации G80 как "локальные" – они не распространяются на весь чип. Однако с другой стороны, это невероятно эффективно для имеющихся возможностей.

   Так что же из себя представляет PDC? Известно, что каждый блок общей памяти состоит из 16 банков однопортовой SRAM. Каждый банк имеет объем 1 KiB (1024 байт) и пропускную способность 32 бита на такт. Так как у G80 всего 16 мультипроцессоров – всего это 256 KiB памяти с пропускной способностью более 675 GiB/s. Для всех задач и целей это можно рассматривать как логическое и очень гибкое расширение регистрового файла.

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

В этом абсолютно произвольном примере, при параллельном кэше для чтения из DRAM требуется всего 4 операции чтения, а в предыдущих вариантах GPGPU – 6 операций. Такой кэш может обеспечить большее быстродействие, по сравнению с обычным кэшем с автоматическим заполнением, но программисту придется проделать большую работу, чтобы заставить эту конфигурацию работать

   Нужно заметить, что кэш параллельных данных поддерживает связь внутри группы из 16 тредов, без какой-либо явной синхронизации. Это можно назвать "суперлокальная неявная синхронизация", и это очень похоже на архитектуру пиксельных шейдеров, которая позволяет выполнять ddx и ddy команды с высокой пропускной способностью и большой скоростью.

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

[N4-Новые концепции для CUDA. Часть 2]    Давайте посмотрим, как NVIDIA описывает среду программирования CUDA. Довольно наглядно это показано на следующем рисунке:

Много ядер, много каналов, много тредов - а это уже не больший параллелизм, чем тот, которым мы ещё можем управлять? " -- David Kirk, NVIDIA

   Термин 'kernel' (ядро ОС) взят от потоковых процессоров. CPU делегирует "ядро" на GPU, и оно там разбивается на сети, блоки и треды. Кроме того, на этом рисунке не показано, что треды (Thread) в блоках группируются в "четверки".

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

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

   После этого все блоки группируются в одну сеть, которая объединяет все треды, выделенные GPU под одно программное ядро. Программа GPU в любое время имеет доступ к уникальному идентификатору задействованного блока и треда. Если Вы хотите, чтобы одновременно работали несколько программных ядер (что действительно нужно в мульти-GPU конфигурации), нужно, чтобы CPU работал в многопоточном режиме.

Синхронизация



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

   __syncthreads() просто позволяет Вам устанавливать точку синхронизации – барьер, и до того, как все треды блока не закончат выполнение всех предыдущих инструкций, ни один последующий код не будет выполняться (блокирование кода, относящегося как к общей памяти, так и к памяти устройства). Это позволяет эффективно предотвратить всевозможные отказы общей памяти "чтение ещё до записи", а также памяти устройства, если другие блоки не работают с теми же самыми ячейками памяти.

   Теоретически, эта команда почти не заметна; она занимает не больше времени, чем любая другая одиночная скалярная операция (2 цикла для 32 тредов), но здесь есть небольшая загвоздка. Чтобы понять проблему, представьте себе, что только арифметико-логические устройства имеют около 10 ступеней (с точки зрения шедулера это 5 ступеней, так как он работает на половинной частоте). Когда Вы запускаете синхронизацию, Вы хотите сделать так, чтобы все треды были в одной и той же точке процедуры. Это означает, что вы сдвигаете конвейер!

   При сдвиге конвейера Вы теряете минимум столько рабочих тактов, сколько стадий в этом конвейере. И это даже без учета задержек, что ещё больше усложняет ситуацию. Если, например, считывание данных из памяти для одной из четверок не закончилось, то все другие четверки для поддержки синхронной работы будут ждать окончания этой операции! Понятно, что это плохо. Можно ли эту проблему полностью устранить? Нет. Можно ли устранить её хотя бы частично? Да.

   Основная идея в том, что если у вас для этой операции задействованы 2 блока на один мультипроцессор, можно надеяться, что у Вас ещё достаточно много других тредов, чтобы обеспечить приличное быстродействие, в то время как другие блоки засинхронизированы. Если, например, у вас работает 8 блоков на одном мультипроцессоре и синхронизации достаточно редки, то есть практически никогда не совпадают по времени, для того чтобы спрятать задержки памяти, у Вас все еще имеется 87,5% тредов.

   Так это работает для локальной синхронизации. Если Вам "без вариантов" нужна глобальная синхронизация, помните, что Вы можете просто распределить нагрузку на несколько программных ядер и заставить CPU осуществлять синхронизацию. Если в каком-то конкретном случае это сделать нельзя, или это будет неэффективно, то треды могут коммутировать друг с другом через память устройства, что очень похоже на то, как если бы задача решалась с помощью CPU. Только убедитесь в том, что Вы знаете, какие блоки в данный момент в вашей архитектуре GPU работают, так как число мультипроцессоров может быть разным!

   Методики предотвращения сбоев "чтение потом запись" в этом случае такие же, как и для CPU, хотя у последнего может быть задействован общий кэш L2. У CUDA вообще нет кэша с чтением/записью, поэтому для организации общей синхронизации нужно учитывать многочисленные задержки памяти. Очевидно, что в идеале нужно иметь быстродействующую локальную синхронизацию и глобальную синхронизацию, организованную на уровне кэша второго уровня, но, похоже, что с G80 мы этого ещё не имеем.

[N5-Что в сумме]   Теперь, когда мы знаем, что из себя представляет CUDA и что она может, давайте подведем краткие итоги:

  • CUDA дает возможность задействовать архитектуру NVIDIA G80 для решения оптимальных для GPU задач с помощью языка, который очень близок к ANSI C и его расширению. Это совсем не то, что предлагает AMD с её CTM, в котором нужно писать программы на ассемблере, что вынуждает прибегать к услугам сторонних разработчиков. Поэтому сравнить сейчас эти продукты напрямую нельзя.

  • G80 имеет 16 независимых 'мультипроцессоров', каждый из которых состоит из 8 'процессоров' (ALU - арифметико-логическое устройство), которые работают на удвоенной частоте шедулера. Каждый мультипроцессор имеет 16 KiB пул "расшаренной памяти", и каждый процессор имеет свой собственный банк регистрового файла. По понятным причинам, у GeForce 8800 GTS активировано только 12 мультипроцессоров.

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

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

  • Если у мультипроцессора задействован не один блок, очень эффективна локальная синхронизация. Кэш параллельных данных также задействуется в схеме коммуникации, когда выполняется синхронизация. Это позволяет уменьшить число передач (=> вмешательство CPU) и увеличить быстродействие.
   Нужно также заметить, что CUDA может обращаться напрямую к OpenGL и DirectX. Кроме того, с помощью CUDA можно задействовать текстурные блоки и, в отличие от обычной памяти с функцией чтения/записи, они кэшируются, поэтому в некоторых случаях их тоже можно задействовать с пользой для дела, так как они всё равно простаивают. К сожалению, сейчас доступна только билине

Источник: www.beyond3d.com

подписаться   |   обсудить в ВК   |