Преобразование uintptr_t в bool замедляет тест SSO в несколько раз - PullRequest
0 голосов
/ 10 сентября 2018

Рассмотрим следующий класс, который реализует (очень в основном для MCVE) оптимизация небольших строк (при условии использования байтов с прямым порядком байтов, 64-битных указателей и т. Д.):

class String {
   char* data_;
   bool sso() const { return reinterpret_cast<uintptr_t>(data_) & 1; }     
 public:
    String(const char * arg = "") {
       auto len = strlen(arg);
       if (len > 6) {
          data_ = new char[len + 1];
          memcpy(data_, arg, len + 1);
       } 
       else {
          data_ = reinterpret_cast<char*>((uintptr_t)1);
          memcpy(reinterpret_cast<char*>(&data_) + 1, arg, len + 1);
       }            
   }  
   ~String() { if (sso() == false) delete data_; }
// ~String() { if (reinterpret_cast<uintptr_t>(data_) & 1 == 0) delete data_; }
};

Обратите внимание, что существует 2 версии деструктора. Когда я измерил разницу между этими двумя версиями с помощью Quick C ++ Benchmark:

static void CreateShort(benchmark::State& state) {    
   for (auto _ : state) {    
      String s("hello");
      benchmark::DoNotOptimize(s); 
   }  
}

Я получил в 5,7 раза быстрее время работы во втором случае с GCC. Я не понимаю, почему компилятор не может генерировать такую ​​же оптимизированную сборку здесь. Что мешает оптимизации компилятора в случае, если результат побитовой операции И дополнительно конвертируется в bool? (Хотя я не эксперт по ассемблеру, я вижу некоторые различия в выходах сборки для обоих вариантов, но не могу понять, почему они существуют.)

С Clang разницы нет, и оба варианта быстрые.


Проблема с преобразованием в bool, а не с встраиванием. Деструктор следующей формы вызывает ту же проблему:

~String() { if ((bool)(reinterpret_cast<uintptr_t>(data_) & 1) == false) delete data_; }

1 Ответ

0 голосов
/ 10 сентября 2018

Для этого кода:

if (reinterpret_cast<uintptr_t>(data_) & 1 == 0) delete data_;

его можно полностью оптимизировать: 1 == 0 всегда 0, а x & 0 всегда ложно для всех x.Первый случай медленнее, потому что он действительно что-то делает.

Полагаю, вы имели в виду:

if ( (reinterpret_cast<uintptr_t>(data_) & 1) == 0) delete data_;

Мнемоника, которую я использую для старшинства & |, состоит в том, чтобы напомнить, что в предшественниках C не было отдельных операторов& и &&;оператор & выполнил обе роли (и вы вручную преобразуете в логический диапазон, если хотите логического сравнения).Таким образом, x == y & z == w был нормальным кодом для проверки того, выполняются ли эти два равенства.

Когда был введен &&, чтобы избежать взлома существующего кода, && был присвоен меньший приоритет, чем &;но & остался без изменений, ниже ==.

Язык C ++ также не изменил эти приоритеты, предположительно, это было сделано с целью минимизации несовместимости между этими двумя языками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...