Russian Qt Forum

Qt => Общие вопросы => Тема начата: Igors от Июль 14, 2020, 14:23



Название: Выжать скорость
Отправлено: Igors от Июль 14, 2020, 14:23
Добрый день

Профайлер показывает что притормаживают такие фрагменты кода (проблема типовая)
Код
C++ (Qt)
void SetUniformFloat( QOpenGLShaderProgram * shader, const char * name, float value )
{
int loc = shader->uniformLocation(name);
Q_ASSERT(loc >= 0);
shader->setUniformValue(loc, value);
}
 
За счет того что таких вызовов много. Имена могут быть также именами структур/массивов напр
Код
C++ (Qt)
SetUniformFloat(shader, "diffuse", 0.5f);  // переменная
SetUniformFloat(shader, "color[0]", 0.5f);  // массив
SetUniformFloat(shader, "light[1].amout", 0.5f);  // массив структур
 
Поэтому приходится еще какое-то время тратить на создание имен с индексом

Как ускорить? Очевидно нужно кешировать location и value. Но прямолинейный подход, напр
Код
C++ (Qt)
QHash<std::string, QPair<int, float>> mHash;
практически не ускоряет, теперь время тратится на создание и сравнение std::string

Ну и по ходу дела (optional): стали бы Вы тут обобщать? Какие-то основания для этого есть - придется написать
Код
C++ (Qt)
SetUniformInt
SetUniformFloat
SetUniformVector3D
SetUniformVector4D
SetUniformMatrix3x3
SetUniformMatrix4x4
Оба (Qt и я) считают что нет, связываться с template здесь не стоит. А Вы ?  :)

Спасибо


Название: Re: Выжать скорость
Отправлено: Авварон от Июль 14, 2020, 15:06
теперь время тратится на создание и сравнение std::string

Так не создавайте


Название: Re: Выжать скорость
Отправлено: __Heaven__ от Июль 14, 2020, 18:38
есть у меня ощущение, что в QOpenGLShaderProgram, glGetUniformLocation используется примерно та же мапа для поиска location по имени. Ещё думаю, не стоит исключать то, что может быть происходит обращение с видеокартой, на что тоже тратится время

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

Я пробовал объединять QOpenGLShaderProgram с её location в одном классе, у которого были методы setModelViewMatrix, setColor и т.д


Название: Re: Выжать скорость
Отправлено: Igors от Июль 15, 2020, 10:12
...может быть происходит обращение с видеокартой, на что тоже тратится время
Происходит, это факт известный ((пере)установка большого кол-ва униформов заметно тормозит)

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

Я пробовал объединять QOpenGLShaderProgram с её location в одном классе, у которого были методы setModelViewMatrix, setColor и т.д
Вы исходите из предположения что шейдер - нечто сопливое, десяток-другой строк :) Есть конечно и такие, но есть и по 1-2К строк. А главное - самих шейдеров много, и сейчас их число бурно растет. Организовывать какие-то структуры/классы для каждого - не вариант. Впрочем и для единичного случая вряд ли катит. Напр шейдер получает переменное число структур lights - и отделаться членами класса для хранения location не удается.

Кстати именно сейчас занимаюсь шейдером для отрисовки "wireframe over" (вместо glPolygonOffset что мы обсуждали в прошлом)

Так не создавайте
Каким образом?  :)

А что же с "обобшением"? Наверное оно уже не модно  :)


Название: Re: Выжать скорость
Отправлено: Авварон от Июль 15, 2020, 11:54
Каким образом?  :)

А что же с "обобшением"? Наверное оно уже не модно  :)

Вам уже мильен раз рассказывали про std::string_view (https://en.cppreference.com/w/cpp/string/basic_string_view)
Если вы poor man (т.е. у вас pre c++17), то можно использовать QLatin1String, опционально может понадобиться написать хэш-функцию.


Название: Re: Выжать скорость
Отправлено: Igors от Июль 15, 2020, 13:48
Вам уже мильен раз рассказывали про std::string_view (https://en.cppreference.com/w/cpp/string/basic_string_view)
Если вы poor man (т.е. у вас pre c++17), то можно использовать QLatin1String, опционально может понадобиться написать хэш-функцию.
Как у Вас лихо получается: надо лишь знать нужный класс - и дело в шляпе, все уже и решено :)

Я использовал незатейливое const char * в роли ключа, т.е.
Код
C++ (Qt)
QHash<const char *, QPair<int, QVariant>> mHash;
 
И ДА. это дало желанный прирост в скорости. Но цена/расплата ужасна, отстреливается уже не "нога", а самое дорогое.
Код
C++ (Qt)
static const std::string u_diffuse("diffuse");
...
auto & data = mHash[u_diffuse.c_str()];
Все хорошо, адрес сишной строки никуда не убежит. Однако
Код
C++ (Qt)
std::string str("diffuse");
...
...mHash[str.c_str()]...
Ошибка, если в хеше уже есть "diffuse", то найдена она не будет, т.к. поиск по адресу (а не содержимому). Использование "современных фишек", думаю, никак не изменит ситуевину, т.к. в любом случае предполагается неизменность/константность ссылки. Для имен что я заранее не могу объявить константами (массивы, структуры) пришлось делать мапу в которой значения - неперемещаемые строки.

Можно ли избежать этого гемора ?


Название: Re: Выжать скорость
Отправлено: Авварон от Июль 15, 2020, 14:44
Можно ли избежать этого гемора ?

Можно, нужно всего

лишь знать нужный класс - и дело в шляпе, все уже и решено :)


Название: Re: Выжать скорость
Отправлено: __Heaven__ от Июль 15, 2020, 18:11
Код:
но есть и по 1-2К строк
Но не по 1К юниформов, подозреваю

Код:
отделаться членами класса для хранения location не удается
Не понимаю, почему в const char * name удаётся отделаться строками. Вектор не мог бы хранить несколько location для этих структур?

Как вариант завести структуру со всевозможными location и вместо const char * name использовать её поля. Уродливо, но зато быстро


Название: Re: Выжать скорость
Отправлено: __Heaven__ от Июль 15, 2020, 18:13
glPolygonOffset, кстати у меня прям норм прижился. Немного повозился с подбором параметров на разных картах и больше не возвращался к нему.


Название: Re: Выжать скорость
Отправлено: Igors от Июль 17, 2020, 05:01
Не понимаю, почему в const char * name удаётся отделаться строками. Вектор не мог бы хранить несколько location для этих структур?

Как вариант завести структуру со всевозможными location и вместо const char * name использовать её поля. Уродливо, но зато быстро
Ну хорошо, вот метод установки униформов
Код
C++ (Qt)
struct MyShader : public QOpenGLShaderProgrram  {
 
void SetUniform( const  char * name,   // uniform or struct field name
const  QVariant & value,      // value to set
const char * structName = 0, // struct or array name
int structIndex = -1 ); // array index  
 
};
 
Примеры использования
Код
C++ (Qt)
shader->SetUniform("diffuse", QVariant(0.5f));  // установка uniform с именем "diffuse"
shader->SetUniform("amout", QVariant(0.5f), "light", 1);  // установка "light[1].amout"
Предложите реализацию метода SetUniform или свой метод с тем же ф-ционалом. Спасибо


Название: Re: Выжать скорость
Отправлено: __Heaven__ от Июль 17, 2020, 20:10
я бы отказался от SetUniform в пользу методов setDiffuse(float) и setAmountOfLight(size_t index, float), setAmountOfLight(float)

locations при линковке шейдера сохранить в структуру
Код:
Locations {
  int diffuse = -1;
  vector<int>light;
}


Название: Re: Выжать скорость
Отправлено: Igors от Июль 18, 2020, 09:49
я бы отказался от SetUniform в пользу методов setDiffuse(float) и setAmountOfLight(size_t index, float), setAmountOfLight(float)

locations при линковке шейдера сохранить в структуру
Код:
Locations {
  int diffuse = -1;
  vector<int>light;
}
Повторюсь: униформов "достаточно много" чтобы для каждого заводить свою переменную и методы. И что vector<int>? В шейдере light объявлен как
Код:
struct Light {
    int type;
    vec4 viewPosition;
    vec3  amount;
    vec3  specular;
    float coneInner;
    float spotCosCutoff;
    vec3  spotDirection;
    float spotExponent;
    float dropoffDist;
    float dropoffOver;
};
Неясно как Вы собираетесь заряжать данные для массива light'ов.


Название: Re: Выжать скорость
Отправлено: __Heaven__ от Июль 18, 2020, 13:26
Да, я понял, что много юниформов. Соответственно и locations будет много + возможно много избыточных

Цитировать
шейдере light объявлен как

Я бы в таком случае использовал

Код:
Locations {
    vector<LightLocations> light

Цитировать
Неясно как Вы собираетесь заряжать данные для массива light'ов
Не работал с массивами, кроме как QVector3D, QMatrix4x4 и подобными - не знаю как выглядит эта зарядка. Я так понял по аналогии с этой
Цитировать
SetUniformFloat(shader, "light[1].amout", 0.5f);

Вместо "light[1].amout" брать locations.light[1].amount


Название: Re: Выжать скорость
Отправлено: Igors от Июль 19, 2020, 07:46
Вместо "light[1].amout" брать locations.light[1].amount
Если я правильно понял Вы предлагаете как бы создать "образ" (или image) шейдера в C++ коде, т.е. те же переменные, структуры и массивы хранят location. Точнее пару (location + QVariant) т.к. location вычисляется при загрузке а значение меняется часто.

На мой взгляд как-то безыдейно. Заводить 100-150 переменных не имеющих никакой смысловой нагрузки не есть хорошо, таким место в контейнере. Да и заботы с тучей переменных найдутся (нужно как-то привязать строковые имена, синхронизировать размеры массивов и.т.п).

Собсно теперешний вариант меня вполне устраивает
Код
C++ (Qt)
void SetUniform( const  char * name,   // uniform or struct field name
const  QVariant & value,            // value to set
const  char * structName = 0, // struct or array name
int structIndex = -1 );       // array index  
 
Накидал строковых констант и заряжай себе. Неприятность только одна: провоцируется "отстрел ноги", для name(s) нельзя использовать даже строковый литерал. Как порешать эту злополучную "семантику" ?


Название: Re: Выжать скорость
Отправлено: Авварон от Июль 19, 2020, 13:17
Неприятность только одна: провоцируется "отстрел ноги", для name(s) нельзя использовать даже строковый литерал. Как порешать эту злополучную "семантику" ?

Достаточно просто взять "нужный класс". Блин, я не понимаю, почему у стольких людей такая проблема с пониманием view? QString/std::string - есть операторы сравнения, хэширования, но тяжелое создание/копирование. char* - нет операторов сравнение, хеширования (то есть есть, но не те). Какое решение? Сделать тип, который ведет себя как char* (то есть не овнит, дешево создается/копируется) и который содержит _нужное_ поведение - сравнивает строки как строки, а не как указатели. Ба, мы только что изобрели std::string_view (и его аналоги QLatin1String, QStringView, gsl::span<char>, std::span<char>). Не нравится "готовый" класс - напишите свой, полезно для ума, может хоть тогда дойдет зачем это надо.


Название: Re: Выжать скорость
Отправлено: Igors от Июль 20, 2020, 06:39
Сделать тип, который ведет себя как char* (то есть не овнит, дешево создается/копируется) и который содержит _нужное_ поведение - сравнивает строки как строки, а не как указатели.
Ключ - строка уже проверялось, время теряется на копирование и более тяжелое qHash. Нужен именно адрес, но тогда как обеспечить его валидность? 
Код
C++ (Qt)
static const std::string str_param1("param1");
static const std::string str_param2("param2");
Если я обращаюсь к хешу используя именно эти константы - все хорошо. И мне совсем не нужна возможность "сравнивать содержимое", нужно сделать такой доступ невозможным, напр
Код
C++ (Qt)
mHash["param1"]  // должна быть ошибка компиляции
Намечал так

Код
C++ (Qt)
class CStringKey : protected std::string {
public:
explicit CStringKey( const char * );
const char * c_str( void ) const;
};
Или лучше конструировать только через статик метод. Но как-то выглядит уж очень великом :) Какие стандартные средства есть для этого ?


Название: Re: Выжать скорость
Отправлено: DarkHobbit от Июль 20, 2020, 13:41
Профайлер показывает
Если не секрет - что за профайлер, и какая версия IDE?


Название: Re: Выжать скорость
Отправлено: Igors от Июль 20, 2020, 14:18
Если не секрет - что за профайлер, и какая версия IDE?
Обычный "Time Profiler" (OSX Instruments), достигнутое ускорение подтверждается fps'ами что я сам считаю и показываю юверю


Название: Re: Выжать скорость
Отправлено: __Heaven__ от Июль 20, 2020, 18:33
Код:
Ключ - строка уже проверялось, время теряется на копирование
Копирования на этапе поиска я побарывал так:
Код:
std::map<string, string, less<>>
За счёт этого можно пользоваться шаблонным find и копирования не будет


Название: Re: Выжать скорость
Отправлено: Авварон от Июль 20, 2020, 19:55
__Heaven__
Там у товарища хэш. Гетерогенный (в котором можно искать по std::string/char*/qbytearray) хэш так просто не сделать.


Название: Re: Выжать скорость
Отправлено: Igors от Июль 21, 2020, 12:45
Код:
Ключ - строка уже проверялось, время теряется на копирование
Копирования на этапе поиска я побарывал так:
Код:
std::map<string, string, less<>>
За счёт этого можно пользоваться шаблонным find и копирования не будет
Имелось ввиду копирование при вставке в мапу/хеш. См также ниже

Гетерогенный
Какие вумные слова знаете :) Не понимаю что здесь это значит, но не видно оснований отказываться от более быстрого хеша, причем чем проще ключ - тем больше "выйгрышь". Так и получается. Теперь мне нужен лишь "синтаксический сахар" чтобы не путаться с обращениями (сам уже пару раз встревал)

Еще интересная деталь. С массивами я не могу объявить все ключи константами. Т.е.
Код
C++ (Qt)
"light[0].amount"
"light[1].amount"
...
"light[xxx].amount"
Разумеется склейка строки при каждом обращении к хешу не годится. Что предложите?