Какой самый быстрый способ копирования и управления большими плотными 2D-массивами в С++

Я пытаюсь оптимизировать свой код, используя многоядерные процессоры, для копирования любых манипуляций с большими плотными массивами.

Для копирования: у меня большой плотный массив (приблизительно 6000x100000), из которого мне нужно вытащить субмарины 15x100000, чтобы выполнить несколько вычислений по трубе. Труба состоит из множества функций линейной алгебры, которые обрабатываются blas, который является многоядерным. Независимо от того, действительно ли время тянуть данные действительно имеет значение по сравнению с линейной алгеброй, это открытый вопрос, но я бы предпочел ошибиться на стороне осторожности и убедиться, что копирование данных оптимизировано.

Для управления: у меня есть много разных функций, которые манипулируют массивами с помощью элемента или строки. Было бы лучше, если бы каждый из них выполнялся многоядерно.

Мой вопрос: лучше ли использовать правую фреймворк (OpenML, OpenCL), и пусть все волшебство произойдет с компилятором или есть хорошие функции/библиотеки, которые делают это быстрее?

Ответ 1

Ваша стартовая точка должна быть старой доброй memcpy. Некоторые советы от тех, кто долгое время был одержим "копированием производительности".

  • Прочитайте Что должен знать каждый программист о памяти.
  • Оцените свои системы memcpy производительность, например, memcpy_bench функция здесь.
  • Оцените масштабируемость memcpy при запуске на нескольких ядрах, например multi_memcpy_bench здесь. (Если вы не используете многопроцессорный NUMA HW, я думаю, вы не увидите много пользы для многопоточного копирования).
  • Вставьте в вашу систему реализацию memcpy и поймите их. Дни, которые вы нашли большую часть времени, проведенных в одиночном rep movsd, давно прошли; в прошлый раз, когда я смотрел на gcc и компилятор Intel CRT, они оба изменяли свою стратегию в зависимости от размера копии относительно размера кэша процессора.
  • В Intel узнайте о преимуществах инструкций хранилища без кэша (например, movntps), поскольку они могут достичь значительных улучшений пропускной способности против обычного подхода ( вы увидите, что они используются в 4.)
  • Имейте доступ и узнайте, как использовать профилировщик пробоотбора, чтобы определить, сколько времени ваших приложений затрачено на операции копирования. Существуют также более продвинутые инструменты, которые могут смотреть на счетчики производительности процессора и рассказывать вам все о том, что делают различные кеши и т.д.
  • (Продвинутая тема) Помните о TLB и когда огромные страницы могут помочь.

Но я ожидаю, что ваши копии будут довольно незначительными накладными расходами по сравнению с любым тяжелым подъемом linalg. Хорошо знать, что такое цифры. Я бы не ожидал, что OpenCL или что-то другое для CPU магически предложит какие-либо улучшения здесь (если ваша системная memcpy не реализована); ИМХО, лучше разобраться в этом материале более подробно, опираясь на основы того, что на самом деле происходит на уровне инструкций, регистров, строк и страниц кеша, чем отходить от этого, накладывая еще один уровень абстракции сверху.

Конечно, если вы планируете переносить свой код из какой-либо многоядерной библиотеки BLAS, которую вы используете в настоящее время для версии с ускоренной линейной алгеброй с графическим процессором, это становится совершенно другим (и гораздо более сложным) вопросом (см. комментарий JayC ниже). Если вы хотите добиться существенного прироста производительности, вы наверняка должны его рассмотреть.