Программирование на видеокартах GPGPU (1184391), страница 7
Текст из файла (страница 7)
Обе частииспользуют индексацию с нуля.Существуют также и другие форматы хранения разреженных матриц: Block Compressed Sparse RowFormat (BSR), Extended BSR Format (BSRX) и др. Здесь они не рассматриваются.Пример использования библиотеки cuSPARSEФайл cuSparseTest.cu, предлагаемый в качестве примера, взят из описания библиотекиcuSPARSE на сайте NVIDIA. Стоит обратить внимание на процедуру работы с библиотекой. Сначалас помощью вызова функции cusparseCreate(&handle) библиотека инициализируется,причём в результате возвращается дескриптор handle, который потребуется при вызовеопераций библиотеки. Затем создаётся и заполняется дескриптор разреженной матрицы(cusparseCreateMatDescr(&descr)) и указываются её другие свойства (тип:cusparseSetMatType(), начальный индекс: cusparseSetMatIndexBase()).
Делаютсянеобходимые операции с матрицей (в качестве параметра передаётся дескриптор обращения кбиблиотеке: handle), после чего результат копируется на компьютер, освобождается дескрипторматрицы (cusparseDestroyMatDescr()) и производится завершение работы с библиотекой(cusparseDestroy(handle)). Кроме того, в тесте делается и преобразование разреженнойматрицы из одного формата (COO) в другой (CSR) с помощью функции cusparseXcoo2csr().27Общие замечания о сторонних библиотекахВ принципе библиотеки, расширяющие какой-нибудь программный продукт, могут быть трёхвидов: в виде заголовочных файлов (содержат только .h или аналогичные им файлы);статические библиотеки (.lib) с заголовочными файлами; статические библиотеки (.lib) сзаголовочными файлами и динамически подгружаемыми компонентами (чаще всего это — .dllфайлы).
Соответственно, и подключение этих трёх видов библиотек к разрабатываемойпрограмме осуществляется проще или сложнее.Проще всего работать с библиотеками, содержащими только заголовочные файлы. Они должныподключаться на этапе компиляции, поэтому необходимо указывать в параметрах проектадополнительные пути поиска этих файлов (C/C++ | General | Additional Include Directories).
Вслучае статических библиотек — помимо путей поиска файлов — нужно также указать, где лежатстатические библиотеки, и какие из них надо просматривать для поиска функций, иначе даже еслипрограмма будет компилироваться, то собрать её ("слинковать") — не получится. Для этого вLinker | General надо добавить Additional Library Directories, а в Linker | Input | AddtionalDependencies включить необходимые статические библиотеки.Если же помимо статических библиотек существуют ещё и динамические компоненты, то нашапрограмма может успешно откомпилироваться и собраться, но не запустится, поскольку не найдётдинамических библиотек (обычное сообщение в таких случаях при попытке запуска программы:"Не могу найти динамическую библиотеку ...").Поскольку обеспечить запуск готовой программы, требующей других динамических библиотек,можно разными способами (в зависимости от того, запускается ли она отдельно или в рамкахсреды редактирования/компиляции/запуска), действия программиста зависят от конкретнойситуации, но всегда следует помнить, что поиск динамических библиотек начинается с текущегокаталога и продолжается в тех каталогах, что указаны в переменной окружения PATH.Исходя из всего вышесказанного, весьма предусмотрительно начинать сборку программы состоронними библиотеками с компиляции отдельных файлов, тогда станет ясно, какихзаголовочных файлов не хватает или они не найдены.
После успешной компиляции отдельныхфайлов программу можно попытаться собрать, тогда будет понятно, все ли статическиебиблиотеки нашлись (или указаны), в противном случае линкер будет всё время сообщать окаких-нибудь не найденных функциях. Когда сборка завершится успешно, можно пробоватьзапускать созданную программу. Если у неё нет зависимости от динамических библиотек – либовсе они найдены (т.е., находятся в пути поиска), – она запустится.
Если же она требует каких-тодинамических библиотек — надо решать, что делать, чтобы они при запуске были обнаружены(скажем, добавить каталоги в пути поиска или переместить динамические библиотеки или ихкопии в нужное место).Дополнение: отображение разреженных матрицКстати, для разреженных матриц предложены способы их отображения, что позволяет вколлекциях таких матриц (см. например, The University of Florida Sparse Matrix Collection,http://www.cise.ufl.edu/research/sparse/matrices/) использовать красивые"изображения" разреженных матриц, встречавшихся в реальных приложениях.287Библиотека Boost — на примере решенияобыкновенных дифференциальных уравненийГоворя о различных библиотеках для C++, невозможно обойти молчанием библиотеку, которая свычислениями на графических картах пока пересекается довольно слабо, но, тем не менее,заслуживает внимательного рассмотрения.
Это библиотека Boost. Она чрезвычайно объёмна,состоит из большого числа компонентов/разделов и охватывает очень многие потребности(линейная алгебра, геометрия, работа с графами, обработка изображений, псевдослучайныечисла, регулярные выражения, многопоточность и пр.). Влияние этой библиотеки на сам язык C++так велико, что часть её включена в новый стандарт языка C++ (так называемый C++11).Один из её разделов, где задействуется GPU, — это решение обыкновенных дифференциальныхуравнений: odeint.Хотя, вообще говоря, в разных своих разделах библиотека Boost оказывается либо библиотекой счисто заголовочными файлами, либо может ещё включать файлы статических (а иногда — идинамических библиотек), в нашем случае (т.е., раздела odeint) она является чисто заголовочной,поэтому для компиляции исходного кода, использующего её, достаточно добавить в путь поискавключаемых файлов значение $(BOOST_ROOT).
(Здесь предполагается, что эта библиотекаустановлена в каталоге, на который указывает переменная окружения BOOST_ROOT, если такойпеременной нет, можно просто использовать вместо неё путь к этому каталогу).Поскольку библиотека Boost написана на весьма «продвинутом» C++, компиляция примеровприменения odeint в Visual Studio 2008 довольно проблематична. Камнем преткновения является,конечно же, не совсем полная поддержка компилятором новых особенностей языка C++.Компилируемый в VS2008 пример — это файл phase_osc_chain.cu, поэтому разберёмприменение Boost на нём. Файлы thrust_lorenz_ensemble.cu, lorenz_parameters.cu,phase_oscillator_ensemble.cu имеет смысл пробовать с другими компиляторами,например g++ или более новой версией Visual Studio.Следует обратить внимание на то, что автор программы, вероятно, компилировал её под Linux,поскольку в VS2008 применённая им функция получения случайных величин drand48() —отсутствует.
По этой причине её пришлось заменить на приблизительный аналог:double(rand())/RAND_MAX.Поскольку вычисления должны проводиться на GPU, из библиотеки Thrust подключается файлthrust/device_vector.h, а также определения итераторов. Из библиотеки Boostподключаются заголовочные файлы раздела odeint.
Последние содержат довольно глубоковложенные пространства имён, поэтому для них, а также для пространства имён stdиспользуются директивы using.На основе вектора на графическом устройстве (thrust::device_vector<>) определенысиноним для векторов из value_type (называется state_type) и синоним для векторов изsize_t (фактически это вектор целых величин; будет называться index_vector_type).Помимо главной функции и задания нескольких констант в программе определяется класс (новыйтип) объектов phase_oscillators.29В нём определён также функтор типа sys_functor — аналог функции, которая должнавызываться при переборе осцилляторов для каждого отдельного осциллятора. Определениефунктора содержит шаблонный параметр — тип набора Tuple (обратите внимание, чтофунктор может существовать и как __host__-объект, и как __device__-объект).В главной функции создаются вектор начальных условий и вектор частот (оба — одинаковойразмерности N), оба располагаются в памяти CPU (т.к.
это вектора из STL); вектор начальныхусловий заполнен случайными значениями фазы, а вектор частот — линейно убывающимичастотами. Эти векторы переносятся в память графической карты и с помощью пошаговоговычислителя типа Рунге-Кутты начинается интегрирование с постоянным шагом dt от 0 до 10.По завершении интегрирования полученные значения фаз с помощью thrust::copy()копируются в стандартный вывод — по одному значению на строке.Для более подробного ознакомления с odeint можно посмотреть статьи:odeint v2 — Solving ordinary differential equations in C++http://www.codeproject.com/Articles/268589/Solving ordinary differential equations with OpenCL in C++http://www.codeproject.com/Articles/429183/Boost.ComputeКроме решения обыкновенных дифференциальных уравнений в составе Boost может появитьсяещё один раздел: библиотека, известная уже некоторое время под именем Boost.Compute.Boost.Compute — GPU/parallel-computing library for C++ based on OpenCLhttps://github.com/boostorg/computeДокументация к библиотеке Boost.Computehttp://boostorg.github.io/compute/Библиотека Boost.Compute реализует C++-интерфейс к многоядерным CPU и GPGPU-платформамна основе OpenCL, по существу являясь «обёрткой» над вызовами библиотеки OpenCL,обеспечивающей доступ к вычислительным устройствам, контекстам, командным очередям,буферам памяти (подробнее об OpenCL — далее).Библиотека Boost.Compute имеет STL-подобный программный интерфейс, предусматривающийобщие алгоритмы (transform(), accumulate(), sort()) и контейнеры (vector<T>,flat_set<T>), вводит параллельные вычислительные алгоритмы (exclusive_scan(),scatter(), reduce()), т.н.