CUDA(англ.Compute Unified Device Architecture) — програмно-апаратна архітектура паралельних обчислень, яка дозволяє істотно збільшити обчислювальну продуктивність завдяки використанню графічних процесорів фірми Nvidia.
CUDA SDK надає можливість включати в текст програм на С виклик підпрограм, що виконуються на графічних процесорах Nvidia. Це реалізовано шляхом команд, які записуються на особливому діалекті C. Архітектура CUDA дає розробнику можливість на свій розсуд організовувати доступ до набору інструкцій графічного прискорювача й керувати його пам'яттю.
Первинна версія CUDA SDK була представлена 15 лютого2007 року. У основі CUDA API лежить розширена мова C. Для успішної трансляції коду цією мовою, до складу CUDA SDK входить власний C-компіляторкомандного рядка nvcc компанії Nvidia. Компілятор nvcc створений на основі відкритого компілятора Open64.
Первинна версія CUDA SDK була представлена 15 лютого 2007. В основі інтерфейсу програмування додатків CUDA лежить мова С з деякими розширеннями. Для успішної трансляції коду цією мовою до складу CUDA SDK входить власний С-компілятор командного рядка nvcc компанії Nvidia. Компілятор nvcc створений на основі відкритого компілятора Open64 і призначений для трансляції host-коду (головного, керуючого коду) і device-коду (апаратного коду) (файлів з розширенням .cu) в об'єктні файли, придатні в процесі складання кінцевої програми або бібліотеки у середовищі програмування, наприклад, в NetBeans.
В архітектурі CUDA використовується модель пам'яті GRID, кластерне моделювання потоків і SIMD-інструкції. Застосовна не тільки для високопродуктивних графічних обчислень, але і для різних наукових обчислень з використанням відеокарт nVidia. Учені і дослідники широко використовують CUDA в різних областях, включаючи астрофізику, обчислювальну біологію та хімію, моделювання динаміки рідин, електромагнітних взаємодій, комп'ютерну томографію, сейсмічний аналіз і багато іншого. У CUDA є можливість підключення до додатків, що використовують OpenGL і Direct3D. CUDA — кросплатформленість для таких операційних систем як Linux, Mac OS X і Windows.
22 березня 2010 nVidia випустила CUDA Toolkit 3.0, який містив підтримку OpenCL.
Переваги CUDA
У порівнянні з традиційним підходом до організації обчислень загального призначення допомогою можливостей графічних API, у архітектури CUDA відзначають наступні переваги в цій області:
Інтерфейс програмування додатків CUDA (CUDA API) заснований на стандартній мові програмування Сі з деякими обмеженнями. На думку розробників, це повинно спростити і згладити процес вивчення архітектури CUDA
Колективна між потоками пам'ять (shared memory) розміром в 16 Кб може бути використана під організований користувачем кеш з більш широкою смугою пропускання, ніж при вибірці зі звичайних текстур
Більш ефективні транзакції між пам'яттю центрального процесора і відеопам'яттю
Повна апаратна підтримка цілочисельних і побітових операцій
Підтримка компіляції GPU коду засобами відкритого LLVM.
Підтримувані GPU й графічні прискорювачі
Перелік пристроїв від виробника устаткування Nvidia із заявленою повною підтримкою технології CUDA приведений на офіційному сайті Nvidia: CUDA-Enabled GPU Products.
Фактично ж, в даний час[коли?] на ринку апаратних засобів для ПК підтримку технології CUDA забезпечують такі периферійні пристрої:
Концепція CUDA відводить GPU роль масивно-паралельного співпроцесора. У літературі про CUDA основна система, до якої підключений GPU, коротко називається терміном хост (host). Аналогічно сам GPU по відношенню до хосту часто називається просто пристроєм (device). CUDA програма задіює як CPU, так і GPU. На CPU виконується послідовна частина коду і підготовчі стадії для GPU-обчислень. Паралельні ділянки коду можуть бути перенесені на GPU, де одночасно виконуватимуться великою кількістю ниток (потоків). Важливо відзначити ряд принципових відмінностей між звичайними потоками CPU і потоками GPU:
Потік (thread) GPU надзвичайно легкий, його контекст мінімальний, регістри розподіленні заздалегідь;
Для ефективного використання ресурсів GPU програмі необхідно задіяти тисячі окремих потоків, тоді як на багатоядерному CPU максимальна ефективність, зазвичай, досягається при числі потоків, рівному або в кілька разів більшому кількості ядер.
Робота потоків на GPU відповідає принципу SIMD. Проте є суттєва відмінність. Тільки потоки в межах однієї групи (для GPU архітектури Fermi — 32 потоки), так званого варпу (warp) виконуються фізично одночасно. Потоки різних варпів можуть перебувати на різних стадіях виконання програми. Такий
метод обробки даних позначається терміном SIMT (Single Instruction — Multiple Theads). Управління роботою варпів виконується на апаратному рівні. За деякими можливостями нових версій CUDA простежується тенденція до поступового перетворення GPU в самодостатній пристрій, який повністю замінює звичайний CPU за рахунок реалізації деяких системних викликів (у термінології GPU системними викликами є, наприклад, malloc і free, реалізованих в CUDA 3.2) і додавання полегшеного енергоефективного CPU-ядра в GPU (архітектура Maxwell). Важливою перевагою CUDA є використання для програмування GPU мов високого рівня. В даний час[коли?] існують компілятори C++ і Fortran. Ці мови розширюються невеликою множиною нових конструкцій: атрибути функцій і змінних, вбудовані змінні й типи даних, оператор запуску ядра.
Приклад CUDA-програми, що використовує GPU для поелементного складання двох одновимірних масивів:
int sum_host( float * a, float * b, float * c, int n )
{ int nb = n * sizeof ( float ); float *aDev = NULL, *bDev = NULL, *cDev = NULL;
// Виділити пам’ять на GPU.
cudaError_t cuerr = cudaMalloc ( (void**)&aDev, nb ); if (cuerr != cudaSuccess)
{ fprintf(stderr, "Cannot allocate GPU memory for aDev: %s\n" , cudaGetErrorString(cuerr)); return 1;
}
cuerr = cudaMalloc ( (void**)&bDev, nb ); if (cuerr != cudaSuccess)
{
fprintf(stderr, "Cannot allocate GPU memory for bDev: %s\n" , cudaGetErrorString(cuerr));
return 1;
}
cuerr = cudaMalloc ( (void**)&cDev, nb );
if (cuerr != cudaSuccess)
' { fprintf(stderr, "Cannot allocate GPU memory for cDev: %s\n",
cudaGetErrorString(cuerr));
return 1;
}
// Завантажити конфігурацію запуска n потоків
dim3 threads = dim3(BL0CK_SIZE, 1);
dim3 blocks = dim3(n / BLOCK_SIZE, 1);
// Скопіювати вхідні дані з пам’яті CPU в пам’ять GPU.
cuerr = cudaMemcpy ( aDev, a, nb, cudaMemcpyHostToDevice );
if (cuerr != cudaSuccess) { fprintf(stderr, "Cannot copy data from a to aDev: %s\n", cudaGetErrorString(cuerr));
return 1;
}
cuerr = cudaMemcpy ( bDev, b, nb, cudaMemcpyHostToDevice );
if (cuerr != cudaSuccess)
{ fprintf(stderr, "Cannot copy data from b to bDev: %s\n", cudaGetErrorString(cuerr));
return 1; }
// Викликати ядро із заданою конфігурацією для обробки даних
sum_kernel<<<blocks, threads>>> (aDev, bDev, cDev); cuerr = cudaGetLastError() ;
if (cuerr != cudaSuccess) { fprintf(stderr, "Cannot launch CUDA kernel: %s\n", cudaGetErrorString(cuerr));
return 1; }
// Чекати завершення роботи ядра
cuerr = cudaDeviceSynchronize();
if (cuerr != cudaSuccess)
{
fprintf(stderr, "Cannot synchronize CUDA kernel: %s\n", cudaGetErrorString(cuerr));
return 1;
}
// Скопіювати результати в пам’ять CPU
cuerr = cudaMemcpy ( с, cDev, nb, cudaMemcpyDeviceToHost );
if (cuerr != cudaSuccess) { fprintf(stderr, "Cannot copy data from cdev to c: %s\n", cudaGetErrorString(cuerr));
return 1;
// Звільнити виділену пам’ять GPU
cudaFree ( aDev );
cudaFree ( bDev );
cudaFree ( cDev );
return 0;
} ">
Функція (або процедура у випадку Fortran) sum_kernel є ядром (атрибут _global_ або global) і виконуватиметься на GPU по одному незалежному потоку для кожного набору елементів a[i], b[i] і c[i].
Загальним прийомом програмування для CUDA є груповання багатьох потоків в блоки. На це є дві причини. По-перше, далеко не для кожного паралельного алгоритму існує ефективна реалізація на повністю незалежних потоках: результат одного потоку може залежати від результату деяких інших, вихідні дані потоку можуть частково збігатися з даними сусідніх. По-друге, розмір однієї вибірки даних з глобальної пам'яті набагато більша розміру дробового або цілочисельного типу, тобто одна вибірка може покрити запити групи з декількох потоків, що працюють із сусідніми елементами пам'яті. У результаті групування потоків вихідна задача розпадається на незалежні одна від одної підзадачі (блоки потоків) із можливістю взаємодії потоків у межах одного блоку й об'єднання запитів до пам'яті в межах одного X 23
варпа (рис. 2.2). Розбиття потоків на варпи також відбувається окремо для кожного блоку. Об'єднання в блоки є вдалим компромісом між необхідністю забезпечити взаємодію потоків між собою і можливістю зробити відповідну апаратну логіку ефективною й дешевою.
Призначений для трансляції host-коду (коду керівної машини) та device-коду (апаратного коду) (файлів з розширенням .cu) в об'єктні файли, придатні в процесі збирання кінцевої програми або бібліотеки в будь-якому середовищі програмування.
Див. також
OpenCL — відкритий стандарт і API для апаратного прискорення обчислення на GPU та інших однорідних обчислювальних системах
OpenGL — графічне API, що починаючи з версії 4.3 підтримує обчислювальні шейдери
Direct3D — пропрієтарне API DirectX, що підтримує обчислювальні шейдери починаючи з версії DirectX 10
BrookGPU — одна з ранніх спроб створити мову програмування для забезпечення GPGPU
Close to Metal — ранній пропрієтарний низькорівневий API для обчислення на GPU для графічних адаптерів ATI/AMD