GLU-1: SSE2 реализация Vec2

Введение

Старый вариант GLUPoly переименован в GLUPoly-1.0.7z. Ниже описаны отличия GLUPoly-1.1.7z.

Написана реализация Vec2 поверх SSE2, использующая интринсики (помещена в Vec2sse2.h). Старый Vec2.h переименован в Vec2std.h. Новый Vec2.h включает Vec2sse2.h, если компилируется MSVC под x64 или с параметром /arch:SSE2 (или /arch:AVX), иначе включает Vec2std.h.

Некоторые изменения были внесены в Bbox2.h (ускорение построения Bbox2 по набору Vec2).

Проблема 1

Для эффективного использования SSE следует выравнивать данные по 16-байтной границе, поэтому значения типа __m128d (пара 64-битных чисел с плавающей точкой, упакованная в 128-битное значение) выравниваются по 16-байт. Это свойство автоматически передаётся всем структурам/классам, включающим в себя __m128d.

Однако стандартные средства выделения памяти не гарантируют выравнивания нестандартных размеров (т.е., например, new int[100] будет выровнен по 4-байтной границе для 4-байтного int, а вот new __m128d[100] по 16-байтной границе уже может быть и не выровнен).

Простое решение этой проблемы достигается перегрузкой operator new и operator delete (попарно всех нужных форм, избыточные варианты delete требуются из-за того, что в случае бросания исключения из конструктора автоматически вызываются только «симметричные» delete, поэтому в случае отсутствия такого delete память не будет освобождена). Эти перегруженные операторы могут выделять память системно-зависимыми функциями, гарантирующими нужное выравнивание.

Недостаток такого решения — невозможность использования с контейнерами, использующими rebind::other аллокатора (например, std::list).

Принятое решение основано на двух дополнительных классах, размещённых в новом файле AlignedAllocator.h.

AlignedNewDeleteImpl<Align> реализует набор operator new/operator delete, выравнивающих по Align байт (Align == 16 для __m128d и, соответственно, SSE2-версии Vec2). Класс, которому нужно обеспечить выравнивание своих объектов, может наследовать от AlignedNewDeleteImpl. С другой стороны, AlignedNewDeleteImpl является обёрткой системно-зависимых функций выделения памяти (только отсюда они вызываются напрямую).

Замечание. Vec2 для простоты использует public-наследование. С точки зрения принципов использования наследования следует использовать private-наследование, однако оказалось, что using AlignedNewDeleteImpl::operator new и т.д. в public-секции не работает. Пока мне не ясно, с чем это связано. Конечно, можно это обойти, написав явную переадресацию вызовов.

AlignedAllocator<T, Align> реализует STL-совместимый аллокатор, для выделения объектов T средствами AlignedNewDeleteImpl. Его можно использовать непосредственно (Align по умолчанию устанавливается равным __alignof T) в качестве параметра шаблона STL-контейнера. «Правильный» аллокатор решает проблему rebind::other.

Кроме того, для удобства можно определить std::allocator для своего класса, чтобы каждый раз не приходилось указывать аллокатор (т.к. стандартный аллокатор всё равно не подходит, и его использование может оказаться латентной ошибкой), что и сделано для Vec2 путём наследования std::allocator<Vec2> от AlignedAllocator<Vec2>.

Проблема 2

Компилятор может «отказаться» передавать объекты с выравниванием поверх стандартных встроенных типов в функции как копии. Например, void foo (const __m128d&); компилируется, а void foo (__m128d); — нет.

К сожалению, в std::vector нашлась такая функция, а именно — resize (size_type, T), принимающая заполняющее значение как копию. Можно по-разному пытаться обойти эту проблему. В данном случае я выбрал решение «в лоб»: написать свою реализацию динамического массива Vec2: Contour (Contour.h и Contour.cpp). Данная реализация также использует SSE2 (для копирования /заполнения диапазонов), если был включен Vec2sse2.h.

UPDATE 08/12. В стандартной библиотеке C++11 эта функция изменена и теперь принимает ссылку. Таким образом, можно использовать std::vector из современных дистрибутивов STL (например, в составе Visual Studio 2012).

Тестирование

Solution теперь содержит два проекта: исходный GLUPoly и вспомогательный Tests (содержит только один файл test.cpp). Цель второго проекта — аккумуляция unit-тестов, проверяющих код из GLUPoly. На данный момент тестами покрыты Vec2, Bbox2 и Contour. Тесты построены на библиотеке Boost.Test.

Своевременное написание тестов позволяет быстро найти и исправить множество ошибок, способствует повышению производительности труда и качества кода.

Ещё

Добавлены FusedFunctor.h и FusedFunctorTuple.h, но о них я намерен написать в следующий раз (планируется внести некоторые изменения).

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: