Задать вопрос
Lite_stream
@Lite_stream

Член класса/структуры типа uint8_t * или int8_t *, оптимизация?

Всем привет

Правильно ли я понимаю, что, если в классе есть член uint8_t * или int8_t * (char), например, указатель на начало массива, и в теле метода присутствуют разыменования данного члена и дальнейшие присваивания (а возможно даже и цикл присваиваний), то для того, чтобы компилятор мог применить оптимизацию, нужно закешировать член класса в локальную переменную ? Ссылка на обсуждение похожей темы
То есть вместо:
uint8_t * buffer; // Член класса/структуры

buffer[0] = value1;
buffer[1] = value2;
buffer[2] = value3;
...
buffer[n] = valueN;

Писать:
uint8_t * _buffer = buffer;
_buffer[0] = value1;
_buffer[1] = value2;
_buffer[2] = value3;
...
_buffer[n] = valueN;


И второй вопрос: можно ли вместо того, чтобы делать локальную копию этой переменной, просто добавить в сигнатуру метода __restrict, гарантировав, что this не изменится в теле метода ? Потому как, если я правильно понял, согласно 1-му ответу здесь
In that case, writing to this->target[0] would alter the contents of this (and thus, this->target).
может поменять константный в соответствии с сигнатурой метода __restrict указатель this ?

P.S.:
  1. Замерял скорость работы этого участка кода с -O3 - при небольшом количестве присваиваний скорость отличается на 10-15%, а если присваивания происходят, например, в цикле с количеством итераций 10_000+, то скорость отличается в 2 раза
  2. Ну и если заменить uint8_t *, например, на uint16_t *, то версия с кэшированием переменной не даёт никакого прироста производительности
  • Вопрос задан
  • 415 просмотров
Подписаться 1 Простой Комментировать
Решения вопроса 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Тут проблема не в том, что член класса char*, а в том, что запись идет в char*, и из-за strict aliasing правил - это может быть запись куда угодно, в том числе в &this. Кешировать пришлось бы любой член структуры, любого типа.

Эту же проблему можно воспроизвести в меньшем масштабе, если у вас в цикле есть запись в int* и чтение какой-то другой не меняющейся int переменной. Особенно, когда переменная в куче и указатель пришел в параметре функции. Вот компилятор офигеет и будет на каждой итерации ее загружать в регистр заново. Опять же, потому что ну не может он понять, что вот этот вот указатель не указывает на вот эту вот переменную.

Частично это можно решить кешированием, можно попробовать поменять типы кое где. Но делать это не стоит - это та самая преждевременная оптимизация, о которой писал Кнут. Лучше алгоритм хороший и структуры данных правильные в вашей программе выберите. А уже дальше, если профилирование покажет, что вот тут вот узкое место, то можно смотреть на ассемблерный код и думать, как убедить компилятор геренировать что-то более быстрое.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@res2001
Developer, ex-admin
Если нет необходимости в подобной локальной переменной, то не нужно "кэшировать".
На уровне ассемблера все обращения к памяти происходят через регистры, так что в любом случае адрес из указателя будет записан в регистр и этот регистр будет индексироваться.

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

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

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

С restrict можно поиграть, но в чистом С++ его нет, на сколько я знаю, но можно включить расширения в gcc/clang и, возможно его можно будет использовть в плюсовом коде. Но применяйте его лучше сразу к локальному bufferу, а не к this.

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

Войдите, чтобы написать ответ

Похожие вопросы