Обновить
8
0

Пользователь

Отправить сообщение

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.

Вон у Oxygene LLVM правильно прикручен:
https://talk.remobjects.com/t/some-interesting-performance-comparision-island-vs-delphi/24596
Хотя тест скорее неудачный - практического смысла в вычислении на этапе компиляции нет, но он показывает, что оптимизация в LLVM работает.
А по Дельфи/Линукс свежей информации нет, придётся видимо самому тестировать.

Нашёл тесты скорости Линуксового кода - действительно в 2 раза медленнее, правда это 2017 год:
https://helloacm.com/integer-performance-comparisons-of-delphi-win32-win64-and-linux64-for-singlemultithreading-counting-prime-number/

По чатам аж половина - ну ОК, возможно я просто не в курсе, планшеты с Андроидом как-то прошли мимо.
Сейчас больше Линукс интересен, но для Линукса с GUI нужно ещё стороннее дополнение, и качество кодогенерации под вопросом. Все компиляторы кроме виндовых работают через LLVM, который в теории может хорошо оптимизировать, но я слышал, что в Дельфи LLVM работает с перманентным -O0, то есть без оптимизации. Хотя может быть эти слухи уже устарели.

А вот преимуществ и возможностей очень много. В особенности у кроссплатформенного фреймворка FMX

Интересно было бы устроить опрос, сколько процентов дельфистов используют FMX.
А то субьективно у меня тоскливое ощущение, что все эти роскошества нафиг никому не нужны.
Новички всё равно на 100% уверены, что Дельфи устарел - хоть с FMX, хоть без. Старые дельфисты поддерживают уже написанный под VCL код и не хотят вдохновляться новыми, будем уж откровенны, свистоперделками типа "уникальной системы стилей", ради которых весь этот VCL-код нужно переписывать.
Ошибка Embarcadero в том, что они сделали ставку на новичков. Если бы выкинули свистоперделки и обеспечили максимальную совместимость с VCL, тогда от FMX было бы больше пользы.
А так получается, что даже пресловутую кроссплатформенность удобнее прикрутить через Freepascal.

Ага, спасибо.
Я особо вьедливо не сравнивал картинки, потому что в обоих случаях это искусственно привнесённый шум (а не например lossless против lossy-сжатия).

Изначально собиралось под Линукс, в исходном архиве есть бенчмарки, но похоже я по незнанию попортил кроссплатформеность.
Сейчас заменил getch на getchar, immintrin.h перенёс внутрь vExt_proc.
Для экзотических платформ нужно задать vExt_proc 0 и tracerProc tracer3, чтобы собрать автовекторизованную версию вместо векторных расширений, которые местами используют интринсики.

У меня основной код был написан давно, в 2017-2018. Всё никак не мог статью дописать, хотелось именно статью, а не просто выложить на форуме. Дотянул до того, что форума уж нет...
И я помню, что например Clang 5 справлялся с векторизацией. А, ну есть же godbolt, можно точно сказать:
https://gcc.godbolt.org/z/ra5MTnfzb
Clang 3.3 и gcc 5.1 нужны для векторизации, 2013-2015 годы.
Но действительно, неустойчиво оно тогда работало, вот этот вариант - уже Clang 3.9 и gcc 4.7
https://gcc.godbolt.org/z/s3bPEzsrr
В том и смысл статьи, показать, что современные компиляторы менее капризны и при грамотном тюнинге кода многое делают сами.

Это конкретно дельфийский компилятор так развивается - по стандартам x64 положено считать плавающую точку на SSE, поэтому её, так и быть, сделали на SSE. А x86? Да ну, чот страшно трогать, поломаем ещё... оставляем FPU. Отсюда и 2 раза.
У FPC, насколько помню, таких заморочек нет, там x86 компилятор может использовать SSE. Хотя наверное, его нужно принудительно включить.

Версия Irfan и сейчас стоит 4.10. Много раз пытался посвежее поставить, но тогда только у этой версии была killer-фича: почти мгновенно (а не через несколько секунд, и после запуска приложения) по нажатию Enter в Проводнике открыть файл на весь экран. Ну и тут же полистать без задержек остальное содержимое папки.

Сочетание быстрого запуска с быстрым листанием кстати мало у кого есть, я даже свой вьюер писал специально для этого, хотя на публику так и не зарелизил, а сейчас уже лень исправлять известные глюки.
Ирфан по-моему не делает предзагрузку следующей, поэтому 16-мп фотки листает всё-таки с небольшой задержкой. В версии 4.10 к тому же не использовалась libjpeg-turbo (появилась с 4.30), там должно быть совсем грустно. Но если картинки мелкие, тогда конечно без разницы.

Информация

В рейтинге
5 251-й
Зарегистрирован
Активность