Воспользовавшись SSE и другими расширениями ЦП

Theres - это пара мест в моей базе кода, где одна и та же операция повторяется очень много раз для большого набора данных. В некоторых случаях это занимает значительное время, чтобы обработать их.

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

  • Есть ли способ компиляции и независимый от ОС способ записи кода, чтобы воспользоваться инструкциями SSE? Мне нравятся встроенные функции VС++, которые включают операции SSE, но я не нашел никаких кросс-компиляторов.

  • Мне все еще нужно поддерживать некоторый процессор, который либо не имеет, либо имеет ограниченную поддержку SSE (например, Intel Celeron). Есть ли способ избежать необходимости создавать разные версии программы, например, иметь какой-то "компоновщик времени выполнения", который связывает либо базовый, либо оптимизированный SSE код на основе процессора, запускающего его при запуске процесса?

  • Как насчет других расширений процессора, глядя на наборы инструкций различных процессоров Intel и AMD, есть несколько из них?

Ответ 1

Для вашей второй точки есть несколько решений, если вы можете разделить различия на разные функции:

  • простые старые указатели функции C
  • динамическое связывание (которое обычно полагается на указатели функций C)
  • Если вы используете С++, различные классы, которые представляют поддержку для разных архитектур и использование виртуальных функций, могут очень помочь с этим.

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

Вот пример использования указателей функций:

typedef int (*scale_func_ptr)( int scalar, int* pData, int count);


int non_sse_scale( int scalar, int* pData, int count)
{
    // do whatever work needs done, without SSE so it'll work on older CPUs

    return 0;
}

int sse_scale( int scalar, in pData, int count)
{
    // equivalent code, but uses SSE

    return 0;
}


// at initialization

scale_func_ptr scale_func = non_sse_scale;

if (useSSE) {
    scale_func = sse_scale;
}


// now, when you want to do the work:

scale_func( 12, theData_ptr, 512);  // this will call the routine that tailored to SSE 
                                    // if the CPU supports it, otherwise calls the non-SSE
                                    // version of the function

Ответ 2

Хорошее чтение по этому вопросу: Остановить войну с набором команд

Краткий обзор: Извините, вы не можете решить свою проблему простым и наиболее совместимым способом (Intel против AMD).

Ответ 3

Собственные функции SSE работают с визуальными С++, GCC и компилятором Intel. В наши дни нет проблем с их использованием.

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

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

Ответ 4

В ответ на ваш комментарий:

Так эффективно, если я не пытаюсь на самом деле выполнять код, содержащий неподдерживаемые инструкции, я в порядке, и я мог бы уйти с "if (see2Supported) {...} else {...} типа?

Зависит. Это нормально для инструкций SSE для существования в двоичном файле, пока они не выполняются. У CPU нет проблем с этим.

Однако, если вы включите поддержку SSE в компиляторе, она, скорее всего, заменит несколько "нормальных" инструкций для их эквивалентов SSE (например, скалярных операций с плавающей запятой), поэтому даже фрагменты вашего обычного не SSE код взорвется на CPU, который его не поддерживает.

Итак, что вам нужно сделать, скорее всего, скомпилировать или два файла отдельно, с включенным SSE и позволить им содержать все ваши SSE-процедуры. Затем свяжите это с остальной частью приложения, которое скомпилировано без поддержки SSE.

Ответ 5

Framewave Build System может скомпилировать двоичные файлы с несколькими путями кода и нацелить несколько платформ.

Ответ 6

Вместо того, чтобы вручную кодировать альтернативную реализацию SSE для вашего скалярного кода, я настоятельно рекомендую вам взглянуть на OpenCL. Это нейтральная для поставщиков переносная кросс-платформенная система для приложений с интенсивным вычислением (и с высокой степенью удобочитаемости!). Вы можете написать свой алгоритм в подмножестве C99, предназначенном для векторных операций, что намного проще, чем ручное кодирование SSE. И лучше всего, OpenCL будет генерировать лучшую реализацию во время выполнения, для выполнения либо на графическом процессоре, либо на процессоре. Таким образом, вы получаете код SSE, написанный для вас.

Theres - это пара мест в моей базе кода, где одна и та же операция повторяется очень много раз для большого набора данных. В некоторых случаях это занимает значительное время, чтобы обработать их.

Ваше приложение похоже на проблему, с которой OpenCL предназначен для решения. Написание альтернативных функций в SSE, безусловно, улучшило бы скорость выполнения, но это большая работа для написания и отладки.

Есть ли способ компиляции и независимый от ОС способ записи кода, чтобы воспользоваться инструкциями SSE? Мне нравятся VС++ intrinsics, которые включают операции SSE, но я не нашел кросс-компиляторов.

Да. Стандартные требования SSE по существу стандартизированы Intel, поэтому одни и те же функции работают одинаково между Windows, Linux и Mac (в частности, с Visual С++ и GNU g++).

Мне все еще нужно поддерживать некоторый процессор, который либо не имеет, либо имеет ограниченную поддержку SSE (например, Intel Celeron). Есть ли способ избежать необходимости создавать разные версии программы, например, иметь какой-то "компоновщик времени выполнения", который связывает либо базовый, либо оптимизированный SSE код на основе процессора, запускающего его при запуске процесса?

Вы можете сделать это (например, используя dlopen()), но это очень сложное решение. Гораздо проще было бы (в C) определить интерфейс функции и вызвать соответствующую версию оптимизированной функции с помощью указателя функции или на С++ для использования разных классов реализации в зависимости от обнаруженного CPU.

С OpenCL это не нужно делать, поскольку код генерируется во время выполнения для данной архитектуры.

Как насчет других расширений процессора, глядя на наборы инструкций различных процессоров Intel и AMD, есть некоторые из них?

В наборе инструкций SSE есть много вариантов. Может быть довольно сложно закодировать один и тот же алгоритм в разных подмножествах SSE, когда некоторые команды отсутствуют. Я предлагаю (по крайней мере, начать), что вы выбираете минимальный поддерживаемый уровень, например SSE2, и возвращаетесь к скалярной реализации на старых машинах.

Это также идеальная ситуация для тестирования единиц/регрессий, что очень важно для обеспечения того, чтобы ваши различные реализации давали одинаковые результаты. Имейте набор тестовых наборов входных данных и известных хороших выходных данных и запустите те же данные через обе версии функции обработки. Вам может потребоваться провести точный тест для передачи (т.е. Разница между результатом и правильным ответом ниже, например, 1e6). Это очень поможет в отладке, и если вы создадите синхронизацию с высоким разрешением в своей тестовой среде, вы можете одновременно сравнить улучшения производительности.