CUDA
CUDA(англ. Compute Unified Device Architecture) — програмно-апаратна архітектура паралельних обчислень, яка дозволяє істотно збільшити обчислювальну продуктивність завдяки використанню графічних процесорів фірми Nvidia.
Тип | GPGPU |
---|---|
Розробник | NVIDIA Corporation |
Стабільний випуск | 9.1 (12 травня 2017) |
Версії | 11.5.1 (21 листопада 2021)[1][2] |
Операційна система | Windows Vista, Windows XP, Windows Server 2008, Windows Server 2003, Linux, Mac OS X |
Ліцензія | власницьке ПЗ |
Вебсайт | Nvidia's CUDA |
CUDA у Вікісховищі |
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 забезпечують такі периферійні пристрої:
Версія специфікації | GPU | Відеокарта |
---|---|---|
1.0 | G80, G92, G92b, G94, G94b | GeForce 8800GTX/Ultra, Tesla C/D/S870, FX4/5600, 360M, GT 420 |
1.1 | G86, G84, G98, G96, G96b, G94, G94b, G92, G92b | GeForce 8400GS/GT, 8600GT/GTS, 8800GT/GTS, 9400GT, 9600 GSO, 9600GT, 9800GTX/GX2, 9800GT, GTS 250, GT 120/30/40, FX 4/570, 3/580, 17/18/3700, 4700x2, 1xxM, 32/370M, 3/5/770M, 16/17/27/28/36/37/3800M, NVS420/50 |
1.2 | GT218, GT216, GT215 | GeForce 210, GT 220/40, FX380 LP, 1800M, 370/380M, NVS 2/3100M |
1.3 | GT200, GT200b | GeForce GTX 260, GTX 275, GTX 280, GTX 285, GTX 295, Tesla C/M1060, S1070, Quadro CX, FX 3/4/5800 |
2.0 | GF100, GF110 | GeForce (GF100) GTX 465, GTX 470, GTX 480, Tesla C2050, C2070, S/M2050/70, Quadro Plex 7000, Quadro 4000, 5000, 6000, GeForce (GF110) GTX 560 TI 448, GTX570, GTX580, GTX590 |
2.1 | GF104, GF114, GF116, GF108, GF106 | GeForce 610M, GT 430, GT 440, GT 640, GTS 450, GTX 460, GTX 550 Ti, GTX 560, GTX 560 Ti, 500M, Quadro 600, 2000 |
3.0 | GK104, GK106, GK107 | GeForce GTX 690, GTX 680, GTX 670, GTX 660 Ti, GTX 660, GTX 650 Ti, GTX 650, GeForce GTX 680MX, GeForce GTX 680M, GeForce GTX 675MX, GeForce GTX 670MX, GTX 660M, GeForce GT 650M, GeForce GT 645M, GeForce GT 640M |
3.5 | GK110, GK208 | GeForce GTX TITAN, GeForce GTX TITAN Black, GeForce GTX 780 Ti, GeForce GTX 780, GeForce GT 640 (GDDR5), GeForce GT 630 v2, Quadro K6000, Tesla K40, Tesla K20x, Tesla K20 |
5.0 | GM107, GM108 | GeForce GTX 750 Ti, GeForce GTX 750 , GeForce GTX 860M, GeForce GTX 850M, GeForce 840M, GeForce 830M |
Ієрархічна модель 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",
сudaGetErrorString(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). Розбиття потоків на варпи також відбувається окремо для кожного блоку. Об'єднання в блоки є вдалим компромісом між необхідністю забезпечити взаємодію потоків між собою і можливістю зробити відповідну апаратну логіку ефективною й дешевою.
Open64
Призначений для трансляції host-коду (коду керівної машини) та device-коду (апаратного коду) (файлів з розширенням .cu) в об'єктні файли, придатні в процесі збирання кінцевої програми або бібліотеки в будь-якому середовищі програмування.
Див. також
- OpenCL — відкритий стандарт і API для апаратного прискорення обчислення на GPU та інших однорідних обчислювальних системах
- OpenGL — графічне API, що починаючи з версії 4.3 підтримує обчислювальні шейдери
- Direct3D — пропрієтарне API DirectX, що підтримує обчислювальні шейдери починаючи з версії DirectX 10
- BrookGPU — одна з ранніх спроб створити мову програмування для забезпечення GPGPU
- Close to Metal — ранній пропрієтарний низькорівневий API для обчислення на GPU для графічних адаптерів ATI/AMD
- Векторизація
- GeForce Tesla V100 — графічний прискорювач, що використовується в навчанні нейронних мереж