Глава 8 Неоднородные вычисления. Неоднородные вычисления Содержание Глава Неоднородные вычисления Основы неоднородных вычислений
Скачать 0.62 Mb.
|
Как это работает... Как пояснялось ранее, наш тест состоит в выполнении нашей задачи вычислений, причём обе в ЦПУ посредством функции test_cpu_vector_sum, а затем через установленное GPU при помощи функции test_gpu_vector_sum. Обе функции выдают отчёт о времени выполнения. Что касается функции проверки на ЦПУ, test_cpu_vector_sum, она состоит из двух вычислительных циклов по 10000 элементам векторов: cpu_start_time = time() for i in range(10000): for j in range(10000): c_cpu[i] = a[i] + b[i] cpu_end_time = time() Значение общего времени ЦПУ составляет разницу между следующим: CPU Time = cpu_end_time - cpu_start_time Что касается функции test_gpu_vector_sum, просматривая ядро выполнения вы можете видеть следующее: __kernel void sum(__global const float *a, __global const float *b, __global float *c){ int i=get_global_id(0); int j; for(j=0;j< 10000;j++){ c[i]=a[i]+b[i];} Значение суммы двух векторов выполняется через единственный цикл вычислений. Полученный результат, как и можно было ожидать, состоит в значительном снижении во времени вычисления для функции test_gpu_vector_sum: (base) C:\> python testApplicationPyopencl.py ============================================================ OpenCL Platforms and Devices ============================================================ Platform - Name: NVIDIA CUDA Platform - Vendor: NVIDIA Corporation Platform - Version: OpenCL 1.2 CUDA 10.1.152 Platform - Profile: FULL_PROFILE -------------------------------------------------------- Device - Name: GeForce 840M Device - Type: GPU Device - Max Clock Speed: 1124 Mhz Device - Compute Units: 3 Device - Local Memory: 48 KB Device - Constant Memory: 64 KB Device - Global Memory: 2 GB Device - Max Buffer/Image Size: 512 MB Device - Max Work Group Size: 1024 ============================================================ Platform - Name: Intel(R) OpenCL Platform - Vendor: Intel(R) Corporation Platform - Version: OpenCL 2.0 Platform - Profile: FULL_PROFILE -------------------------------------------------------- Device - Name: Intel(R) HD Graphics 5500 Device - Type: GPU Device - Max Clock Speed: 950 Mhz Device - Compute Units: 24 Device - Local Memory: 64 KB Device - Constant Memory: 64 KB Device - Global Memory: 3 GB Device - Max Buffer/Image Size: 808 MB Device - Max Work Group Size: 256 -------------------------------------------------------- Device - Name: Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz Device - Type: CPU Device - Max Clock Speed: 2400 Mhz Device - Compute Units: 4 Device - Local Memory: 32 KB Device - Constant Memory: 128 KB Device - Global Memory: 8 GB Device - Max Buffer/Image Size: 2026 MB Device - Max Work Group Size: 8192 CPU Time: 39.505873918533325 s GPU Kernel evaluation Time: 0.013606592 s GPU Time: 0.019981861114501953 s Даже несмотря на то, что наш тест не блещет вычислительной выразительностью, он предоставляет полезное указание на значительный потенциал карты GPU. Также ознакомьтесь... OpenCL является стандартным крос- платформенным API для разработки приложений, которые эксплуатируют параллельное вычисление в неоднородных системах. Примечательна аналогия с CUDA, включая всё, начиная с имеющейся иерархии памяти, вплоть до непосредственного соответствия между потоками и элементами исполнения. Даже на самом уровне программирования имеется множество аналогичных сторон и расширений с одной и той же функциональностью. Однако OpenCL имеет намного более сложную модель управления устройством по причине способности поддержки широкого разнообразия аппаратных средств. С другой стороны, OpenCL разработан для того чтобы получать переносимость между продуктами различных производителей. Благодаря большей зрелости и узкой специализации аппаратных средств, CUDA предлагает более простое управление устройством и API более высокого уровня, которые делают его более предпочтительным, но только когда вы имеете дело с конкретными архитектурами (то есть графическими картами nVidia). В своих последующих разделах мы поясняем за и против библиотек CUDA и OpenCL, а также PyCUDA и PyOpenCL. За OpenCL и PyOpenCL Аргументы за таковы: Они делают возможным использование неоднородных систем различного типа микропроцессоров. Один и тот же код исполняется в различных системах. Против OpenCL и PyOpenCL Аргументы против таковы: Сложное управление устройством API не полностью стабилен За CUDA и PyCUDA Аргументы за такие: API с очень высокими уровнями абстракции Расширения для очень большого числа языков программирования Гигантские объёмы документации и очень большое сообщество Против CUDA и PyCUDA Аргументы против такие: Поддерживает только самые последние GPU nVidia ы качестве устройств Снижает неоднородность ЦПУ и GPU Дополнительно Анлреас Клёкнер сделал ряд лекций по программированию GPU пр помощи PyCuda и PyOpenCL, доступные по ссылкам в www.bu.edu и www.youtube.com. Программирование GPU с применением Numba Numba является компилятором Python, который предоставляет основанный на CUDA API. Он разработан в первую очередь для задания численных вычислений, точно также как и библиотека NumPy. В частности, библиотека numba управляет типы массивов данных, предоставляемых NumPy и обрабатывает их. Действительно, такая эксплуатация параллелизма данных, которая является присущей численным вычислениям с привлечением массивов, является естественным выбором для ускорителей GPU. Компилятор работает с особыми типами сигнатур (или декораторов) для функций Python и делает возможной компиляцию времени исполнения (такой тип компиляции также имеет название Как раз вовремя, JiT - Just In Time). Наиболее важными декораторами являются следующие: jit: Позволяет разработчику писать CUDA- подобные функции. Когда они встречаются, имеющийся компилятор транслирует весь код под декоратором в язык псевдоассемблера PTX с тем, чтобы иметь возможность выполнения в GPU. autojit: Снабжает комментарием функцию для процедуры отложенной компиляции, что означает, что такая функция с этой сигнатурой компилируется всего однажды. vectorize: Создаёт так называемую ufunc (NumPy Universal Function), которая получает некую функцию и выполняет её параллельно для векторных аргументов. guvectorize: Строит так называемую gufunc (NumPy Generalized Universal Function). Объект gufunc может работать с целыми подмассивами. Приготовление Numba (выпуск 0.45) совместим с Python 2.7 и 3.5 или более поздними версиями, а также с версиями NumPy с 1.7 до 1.16. Для установки numba рекомендуется, как и для pyopencl, применять инфраструктуру Anaconda, а потому в приглашении Anaconda просто наберите следующее: (base) C:\> conda install numba Кроме того, для применения всего потенциала numba следует установить библиотеку cudatoolkit: (base) C:\> conda install cudatoolkit После этого можно проверить будут ли определяться как положено библиотека CUDA и GPU. Из приглашения Anaconda откройте интерпретатор Python: (base) C:\> python Python 3.7.3 (default, Apr 24 2019, 15:29:51) [MSC v.1915 64 bit (AMD64)] :: Anaconda, Inc. on win32 Type "help", "copyright", "credits" or "license" for more information. >> Самая первая проверка влечёт за собой проверку того установлена ли как положено библиотека CUDA (cudatoolkit): >>> import numba.cuda.api >>> import numba.cuda.cudadrv.libs >>> numba.cuda.cudadrv.libs.test() Последующий вывод показывает такое качество нашей установки, при котором все проверки возвращают положительный результат: Finding cublas from Conda environment located at C:\Users\Giancarlo\Anaconda3\Library\bin\cublas64_10.dll trying to open library... ok Finding cusparse from Conda environment located at C:\Users\Giancarlo\Anaconda3\Library\bin\cusparse64_10.dll trying to open library... ok Finding cufft from Conda environment located at C:\Users\Giancarlo\Anaconda3\Library\bin\cufft64_10.dll trying to open library... ok Finding curand from Conda environment located at C:\Users\Giancarlo\Anaconda3\Library\bin\curand64_10.dll trying to open library... ok Finding nvvm from Conda environment located at C:\Users\Giancarlo\Anaconda3\Library\bin\nvvm64_33_0.dll trying to open library... ok Finding libdevice from Conda environment searching for compute_20... ok searching for compute_30... ok searching for compute_35... ok searching for compute_50... ok True В своей второй проверке мы удостоверяем присутствие графической карты: >>> numba.cuda.api.detect() Полученный вывод показывает обнаружена ли графическая карта и поддерживается ли она: Found 1 CUDA devices id 0 b'GeForce 840M' [SUPPORTED] compute capability: 5.0 pci device id: 0 pci bus id: 8 Summary: 1/1 devices are supported True Как это сделать... В данном примере мы предоставляем демонстрацию компилятора Numba с применением аннотации @guvectorize. Наша задача состоит в умножении матриц: Импортируем guvectorize из библиотеки numba а также модуль numpy: from numba import guvectorize import numpy as np При помощи декоратора @guvectorize мы определяем ту функцию matmul, которая выполнит задачу умножения матриц: @guvectorize(['void(int64[:,:], int64[:,:], int64[:,:])'], '(m,n),(n,p)->(m,p)') def matmul(A, B, C): m, n = A.shape n, p = B.shape for i in range(m): for j in range(p): C[i, j] = 0 for k in range(n): C[i, j] += A[i, k] * B[k, j] Наша матрица имеет размер 10 × 10, в то время как значения элементов целые: dim = 10 A = np.random.randint(dim,size=(dim, dim)) B = np.random.randint(dim,size=(dim, dim)) Наконец, мы вызываем свою функцию matmul для предварительно определённых входных матриц: C = matmul(A, B) Мы выводим на печать входные матрицы и получаемую в результате матрицу: print("INPUT MATRIX A") print(":\n%s" % A) print("INPUT MATRIX B") print(":\n%s" % B) print("RESULT MATRIX C = A*B") print(":\n%s" % C) |