Как стать автором
Обновить

Комментарии 16

Добавлю свои 5 копеек

SSE не полностью совместим с IEEE 754, поэтому результаты могут отличаться от скалярной версии

Большинство современных процессоров являются скалярными
Поправка — суперскалярными.

SSE и AVX векторы всегда попадают в одну кэш линию, если они выравнены по 16 и 32 байта, соответственно
Невыровненные векторы читать/писать можно, для этого есть специальные версии команд загрузки/сохранения (безусловно, это может отразиться на производительности).
Для векторов, лежащих на границе двух кэш линий есть специальный SSE3 интринсик _mm_lddqu_si128

Сложные алгоритмы потребуют изобретательность, но без этого никуда
именно поэтому я бы сменил показатель «усилия программиста» на умеренно высокие

Ценой малых усилий мы можем получать от системы как можно большей производительности, задействуя все возможные аппаратные ресурсы
на самом деле далеко не все, т.к. используете только узкое подмножество инструкций
ИМХО SIMD создан не для того, чтобы «не надо было думать», а для того, чтобы выжать максимум из конкретного CPU.

вы действительно получаете кросс-процессорную поддержку на С#
А как будут обстоять дела с ARM?
Поправка — суперскалярными


Насчет суперскалярных согласен, просто не хотел упоминать об этом конкретно, а сосредоточиться чисто на понятии скалярного процессора от векторного, и что производители CPU предпринимают для улучшения качества продукции.
Про выравнивание данных я выше писал, что компилятор может использовать инструкции для которых не требуется выравнивание данных: например, movups. Просто на мой взгляд выравнивание стоит делать, если есть возможность. В ссылках на статьи, в одной из которых упоминается про выравнивание, также рекомендуется делать это. По большей части это относится к тем, кто работает с неуправляемой памятью. Но в .NET я также видел ситуации, когда стоит делать выравнивание.
Про малые усилия, тут я имел ввиду, что для выполнения к примеру одного и того же алгоритма при помощи потоков или аппаратной поддержки, то в явном выигрыше аппаратная поддержка. Не нужно создавать потоки и управлять ими, меньше нагрузка на планировщик потоков и больше процессорного времени. Но при этом получаем равную или же даже лучшую производительность. Про использование аппаратных ресурсов, пусть даже только их узкое подмножество, то для .NET разработчиков это очень приятное дополнение. На плюсах векторизация была всегда, а на C# только появилась, что радует.
Для ARM есть технология SIMD, но тут она не поддерживается. Только x64. Я думаю под кросс-поддержкой имеется ввиду то, что происходит автоматический выбор SIMD-расширений, а в случае их отсутствия режим работы с обычной производительностью скалярного процессора.
Как видите, довольно много ограничений, но аппаратная поддержка в управляемом языке, это интересно.
SSE не полностью совместим с IEEE 754, поэтому результаты могут отличаться от скалярной версии

а вот про это можно поподробнее, где бы посоветовали почитать?
где бы посоветовали почитать?
Например,
Intel 64 and IA-32 Architectures Optimization Reference Manual
3.8.3.3 Floating-point Exceptions in SSE/SSE2/SSE3 Code
Однако, подход с C# намного проще, многие вещи сделаны за Вас и Вам только остается пользоваться и наслаждаться.

Это неправда. Для вашего примера, решенного в лоб:
#include <iostream>

int main()
{
    const auto N = 8;

    float a[] = { 41982.0,  81.5091, 3.14, 42.666, 54776.45, 342.4556, 6756.2344, 4563.789 };
    float b[] = { 85989.111,  156.5091, 3.14, 42.666, 1006.45, 9999.4546, 0.2344, 7893.789 };
    float c[8];

    for(auto i=0;i<N;++i) {
        c[i]=a[i]+b[i];
    }

    std::cout.precision(10);
    for (size_t i = 0; i < N; i++)
        std::cout << c[i] << std::endl;
}

компилятор с вектороной оптимизацией (напр. g++ -O3) сгенерирует следующий код для цикла:
addps  xmm1,xmm3
addps  xmm0,xmm2
Я выше упоминал, что в С++ присутствует автоматическая векторизация. А вот компилятор C# не сделал бы этого ни при каких оптимизациях. Заранее сложный пример был приведен для того, чтобы подробнее объяснить принцип работы и управлением вручную, а затем как это сделано в C#. Статья больше ориентирована на .NET, С++ здесь как большой брат и исключительно в качестве примера базиса.
Ну, в простых случаях авто-векторизация работает. Но далеко не факт, что в конкретном приложении будет такой же простой для векторизации код. Ведь компилятор должен доказать, что такая трансформация безопасна (например из-за зависимостей), а это доказать получается далеко не всегда. по этому зачастую осмысленно применение прагм явной векторизации.
можно было бы еще добавить компилятору опцию использовать AVX2, т.е. более широкие вектора.
Саша вообще очень крутой! И в общении приятный.

Выступал у нас на DotNext c двумя докладами, один из которых как раз было про SIMD. Стал лучшим докладчиком по мнению участников :) В феврале-марте — напишу на хабре и видосы выложу.

Презентация доклада Саши про SIMD — тут.
Ого, а почему так поздно, аж февраль-март?
В январе домонтируем видео и выложим на Youtube. А потом мне нужно будет просмотреть топовые и написать статью. Все это требует времени, к сожалению.
эта ссылка возвращает:
The file you’re looking for has been moved or deleted.
Please see this article for details on why a shared link might stop working.
Сорри, вот правильная ссылка.
Сорри, вот правильная ссылка.
не туда
может я чего то не доглядел, но строчка
Console.WriteLine("Vector<char>.Count: {0}", Vector<Char>.Count);

во время выполнениея выдает ошибку:
System.TypeInitializationException: The type initializer for 'System.Numerics.Vector`1' threw an exception.
----> System.NotSupportedException: Specified type is not supported


кроме того комментарии из примера не соответствуют моим тестам
for (int i = 0; i < N; i += Vector<Single>.Count) // Count возвращает 16 для char, 4 для float, 2 для      double и т.п.


<blockquote>Vector<Single>.Count: 8
Vector<int>.Count: 8
Vector<double>.Count: 4</blockquote>
Чтобы не было исключения, сборка должна быть релизной. На Debug у меня такая же ситуация.
Затем, как я выше упоминал, JIT сам выбирает какой SSE использовать или AVX. В вашем случае система выбрала AVX, а там 256-битный регистр. У вас соответственно больше элементов будет обрабатываться за один раз. В моем случае система выбрала SSE, там 128-битные регистры.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации