Pull to refresh
8
0
Send message

Да, я заметил, но при желании легко исправить на полную сортировку, нужно переделать функцию Sort(__m128i a[9]).
Там сейчас 19 сравнений. Пишут, что для полной сортировки 9 элементов нужно 25, схема есть:
https://www.sciencedirect.com/science/article/pii/S0022000015001397
Как раз преимущество подхода в том, что легко переделать на разные сортировочные сети, не придумывая каждый раз схему перемещения элементов в xmm-регистрах.

Ещё в медианном фильтре картинок этот алгоритм применяется:
https://habr.com/ru/articles/204682/
там кстати код попроще выглядит, без перетасовок в xmm-регистрах. Используется векторизация в направлении "по массиву" (пикселей) - алгоритм абсолютно аналогичен скалярному, только вместо одного значения работаем с пачками по 16-32 элементов. Поэтому получается всего 6 интринсиков, можно даже и без них, у меня где-то был автовекторизованный вариант.
Но да, это подходит для пикселей, а у вас большой пачки из сортируемых фрагментов может и не быть в наличии.

Покажу как я прикручивал инлайн к кардтрейсеру. И конечно проверял - да, заинлайнилось, но медленнее на 2 секунды.

И что не так?
  function Add(Const v1, v2: TVector): TVector; overload; inline;
  begin
    Result.x := v1.x + v2.x;
    Result.y := v1.y + v2.y;
    Result.z := v1.z + v2.z;
  end;

  function Dot(Const v1, v2: TVector): TFloat; overload; inline;
  begin
    Result := v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
  end;

  function Dot(Const v1: TVector): TFloat; overload; inline;
  begin
    Result := v1.x * v1.x + v1.y * v1.y + v1.z * v1.z;
  end;

  function Init(x,y,z : TFloat): TVector; inline;
  begin
    Result.x := x; Result.y := y; Result.z := z;
  end;

// в tracer:
        p := o + TVector.Create(-k, 0, -(j + 4));
        b := p * d;
        c := p * p - 1;
        q := b * b - c;
// ->
        p := Add(o, Init(-k, 0, -(j + 4)));
        b := Dot(p, d);
        c := Dot(p) - 1;
        q := b * b - c;

Новые (другие) типы. Также, использовать новую математику, интринсики и т.д.

Какие конкретно? Про SSE я знаю и объяснил, почему это не сработало.

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

Компилятор Делфи не застрял в 2000-х, вы заблуждаетесь.

У меня есть проект, который собирается разными версиями Дельфи начиная с D5/1999, недавно протестировал его на D12/2023.
И какая же разница в скорости исполняемого кода за 24 года?
Никакой! Ну может 1%, на уровне погрешности.
Потому что там в основном целочисленные расчёты, и единственное заметное улучшение компилятора (плавающая точка на SSE в x64) не влияет.
Так вот и получается, что для моих целей прогресса в компиляторе нет, и он застрял даже можно сказать в 90-х.
Андроиды и прочие платформы не особо интересны, разве что Линукс... впрочем, по сообщениям там ещё хуже со скоростью, потому что это криво (с отключенной оптимизацией) прикрученный LLVM. Кто-то может проверить кардтрейсер на Дельфи/Линуксе?

На Си у меня нет проекта в точности аналогичного дельфийскому, но тот же кардтрейсер на gcc версий 4.9 и 12 (между ними 8 лет) показывает на разных ф-ях ускорение 10-20%, а иногда 2+ раза, если новая версия векторизовала, а старая нет. С компилятором 1999 года разница должна быть ещё больше хотя бы из-за поддерживаемого набора инструкций.

Ну что касается "написать правильно", то например C++ можно относительно небольшими правками разогнать в разы, я про это целую статью писал:
https://habr.com/ru/articles/685228/
Дельфи от этих правок ускоряется гораздо меньше, потому что векторизацию компилятор не умеет совсем. Хотите SIMD - только хардкор, только ассемблерные простыни вручную.

включая C#, который "всего" в 2 раза медленнее нативного Delphi

Навскидку на этом:
https://github.com/Mark-Kovalyov/CardRaytracerBenchmark
получаю С# x64 - 9.4, Delphi x64 - 16.8 секунды (однопоток, консольный вывод в Дельфи отключен). Меньше - лучше, то есть С# быстрее почти в 2 раза.
Почему? Ну, JIT C# основан вероятно на C++, а кодогенерация в плюсовых компиляторах со временем улучшается. У Дельфи она застряла на уровне 2000-х по причине неустроенности (перепродажи) и нехватки ресурсов на разработку.
Я понимаю, почему вы держитесь за написанные и отлаженные млн. строк, просто слегка просвещаю насчёт современного соотношения сил.

Notepad++ даёт небольшую задержку при запуске. Да, едва заметную, может полсекунды, но разница с AkelPad чувствуется. Дело вкуса, но мне хотелось бы мгновенной реакции от контекстного вьюера всякой txt-мелочи.

Тоже когда-то пользовался Bred, потом перешёл на AkelPad - всё то же самое + подсветка, 600 кб.
Сейчас проверил новые версии - уже и автодополнение есть, и сворачивание кода при макс. размере 4.5 мб. Но это плагины, при желании можно пользоваться как блокнотом.

64 Гб/c у Pci-Ex 4 - это суммарная в обе стороны, в одну сторону 32 Гб/c. По моим тестам реальная скорость в оптимальном для OpenCL варианте (pinned memory) около 75% от максимальной, 23-24 Гб/c на GF3080. Возможно, серией асинхронных запросов с разными картинками можно выжать больше, у меня гонялась одна.

Тесты с обработкой я тоже делал, и получалось, что единичные простые операции (напр. gauss blur) всё-таки нет смысла гонять в видеокарту. Чем сложнее, тем лучше: AHD debayer - умеренное ускорение раза в два, нейросети - да, самое то :)
Или переносить на GPU весь конвейер из простых операций. При этом отлаживать и поддерживать GPU-код сложнее.

Советы компиляторы уже дают, в godbolt для Clang есть окно Optimization (через кнопку +).

Насчёт loop distribution он оказался прав, но сам не осилил, пришлось вручную делать.

( https://habr.com/ru/articles/685228/ )

"реализация GetEvensCount без if всё-таки чуть-чуть медленнее, чем с if'ами"

Хотя это и несколько оффтоп, но попиарю в очередной раз компилятор Clang:
https://gcc.godbolt.org/z/WoMbrx8zx
SIMD, развёртка, считаем 32 числа за итерацию - эта версия уж точно не медленнее.
Значит, в общем случае избавляться от if выгоднее.

Только я вставлял в сишный проект, поэтому переправил всё с плюсов на чистый Си. Но вряд ли это влияет. И я тоже ожидал, что будет мухлевать с итерациями, но нет, время явно зависит от Cycles.

код
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

unsigned long sum_array(unsigned long* array, unsigned long size)
{
    unsigned long sum = 0;
    for (unsigned long i = 0; i < size; ++i)
         sum += array[i];
    return sum;
}

int main()
{
    const unsigned long n = 4096;
    const Cycles = 2000000;
    const Cycles2 = 20;
    unsigned long array[n];
    srand(42);

    for (unsigned long i = 0; i < n; ++i)
        array[i] = rand();

    LARGE_INTEGER frequency;
    LARGE_INTEGER t1, t2, t3, t4;

    QueryPerformanceFrequency(&frequency);
    unsigned long s = 0;
    double minTime = 100000000;

    for (int m = 0; m < Cycles2; m++) {
        QueryPerformanceCounter(&t1);
        for (int i = 0; i < Cycles; i++) {
            s = sum_array(array, n);
        }
        QueryPerformanceCounter(&t2);
        double elapsedTime=(double)(t2.QuadPart-t1.QuadPart)/frequency.QuadPart;
        if (elapsedTime < minTime) minTime = elapsedTime;
    }
    printf("sum: %lu\n", s);
    printf("microseconds:      %.5f seconds\n", minTime);

    return 0;
}

Мне удалось выжать почти 2 раза (1.8), но это с минимальным размером массива (4096) и с двумя внешними циклами, один просто крутит 2000000 итераций, другой выбирает минимальное время из 20 попыток. Clang 14 / gcc 12.
В реальных условиях наверное чаще будет 5%, чем 2 раза.

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

Получается, в Skylake уже 3 модуля для векторного сложения (Int Vect ALU), так что Clang где-то и прав.
Я брал информацию по вычислительным модулям у Агнера Фога, может он намеренно упоминал только "настоящие" модули с умножением и FMA:
https://www.agner.org/optimize/blog/read.php?i=838

Clang не согласен, он делает ещё развёртку SIMD-кода.
https://gcc.godbolt.org/z/3M8W81rMv
Можно добавить к статье, что сказанное про множественные скалярные арифметические модули справедливо и для SIMD, у Intel сейчас 2 * 256 бит, у AMD 4 * 128 бит. Получается, развёртка Clang 4 * 256 избыточна, ну вот такая у него "широкая душа", любит развернуть с настройками по умолчанию. Может, в расчёте на будущие процессоры.

Не на том материале рекламируете :)

Naked to the bone

Надо было уточнить - мой опыт относится к Си/Дельфи, Go толком не знаю. Сейчас попытался переписать функцию на Go:
https://gcc.godbolt.org/z/ernMd4qE3
Без проверок диапазонов ассемблер похож, кстати, на Дельфи - векторизации нет, но в целом достаточно чистый код.
И так или иначе он работает гораздо быстрее ввода-вывода png. Я даже проверил:
Загрузка 24-битного png через GDI+: 22 мс
Извлечение канала на Дельфи (имитируем Go): 0.76 мс
Извлечение канала на Си: 0.19 мс
Отсюда и вывод, что не должно копирование байтиков заметно тормозить... не знаю, может автору тоже отключить проверки глобально директивой B-? Или это плохой тон?

В Си простые циклы векторизуются автоматически, интринсики вспоминать не надо.
https://gcc.godbolt.org/z/a9ecW4hx4
Для SIMD конечно лучше, если в исходнике 4 байта на пиксель, меньше команд понадобится (или менее "тяжёлых").

Но по моему опыту, копирование с извлечением синего канала не должно сильно влиять. В основном должно упираться в распаковку/упаковку png, потому что png вообще медленный. Как я слышал, zlib-овский inflate/deflate слабо (или вообще никак) ускоряется через SIMD. У вас получилось 20% на выборе оптимизированной библиотеки - ну вот примерно так, да, и это мало. Качественные библиотеки jpeg ускоряют в разы.

Поэтому главное - грамотное распараллеливание упаковки/распаковки. И у вас хороший результат, я не ожидал, что можно делать чтение 1000 картинок и упаковку одной огромной за 0.23 c.

Information

Rating
Does not participate
Registered
Activity