Есть ли недостаток в том, как clang реализует char8_t, или какой-то темный угол стандартного запрета оптимизации? - PullRequest
12 голосов
/ 16 апреля 2019

clang 8.0.0 представляет поддержку типа char8_t из c ++ 20. Однако я ожидаю, что следующие функции будут иметь одинаковый вывод компилятора

#include <algorithm>

bool compare4(char const* pcha, char const* pchB, int n) {
    return std::equal(pcha, pcha+4, pchB);
}

bool compare4(char8_t const* pchA, char8_t const* pchB, int n) {
    return std::equal(pchA, pchA+4, pchB);
}

Однако они компилируются в -std=c++2a -O2 до

compare4(char const*, char const*, int):   # @compare4(char const*, char const*, int)
        mov     eax, dword ptr [rdi]
        cmp     eax, dword ptr [rsi]
        sete    al
        ret
_Z8compare4PKDuS0_i:                       # @_Z8compare4PKDuS0_i
        mov     al, byte ptr [rdi]
        cmp     al, byte ptr [rsi]
        jne     .LBB1_4
        mov     al, byte ptr [rdi + 1]
        cmp     al, byte ptr [rsi + 1]
        jne     .LBB1_4
        mov     al, byte ptr [rdi + 2]
        cmp     al, byte ptr [rsi + 2]
        jne     .LBB1_4
        mov     al, byte ptr [rdi + 3]
        cmp     al, byte ptr [rsi + 3]
        sete    al
        ret
.LBB1_4:
        xor     eax, eax
        ret

в котором последний явно менее оптимизирован. Есть ли причина для этого (я не смог найти ничего в стандарте), или это ошибка в clang?

Ответы [ 2 ]

4 голосов
/ 17 апреля 2019
  1. В libstdc ++ std::equal вызывает __builtin_memcmp, когда обнаруживает, что аргументы "простые", в противном случае используется наивный цикл for. «Простой» здесь означает указатели (или определенные обертки итератора вокруг указателя) на одно и то же целое число или тип указателя ( соответствующий исходный код )

    • Является ли тип целочисленным типом, определяется внутренней чертой __is_integer, но libstdc ++ 8.2.0 (версия, используемая на godbolt.org) не специализирует эту черту для char8_t, поэтому последняя не обнаруживается как целочисленный тип. ( соответствующий исходный код )
  2. Clang (с этой конкретной конфигурацией) генерирует больше многословной сборки в случае цикла for, чем в случае __builtin_memcmp. (Но первый не обязательно менее оптимизирован с точки зрения производительности. См. Loop_unrolling .)

Так что есть причина для этой разницы, и это не ошибка в Clang IMO.

4 голосов
/ 16 апреля 2019

Это не «ошибка» в Clang;просто упущенная возможность для оптимизации.

Вы можете реплицировать вывод компилятора Clang , используя ту же функцию, взяв enum class, базовый тип которого unsigned char.GCC, напротив, распознает разницу между перечислителем с базовым типом unsigned char и char8_t.Он генерирует один и тот же код для unsigned char и char8_t, но генерирует более сложный код для enum class.

Так что что-то в реализации Clang char8_t, похоже, воспринимается как пользователь-определенное перечисление, чем в качестве основного типа.Лучше всего считать это ранним внедрением стандарта.

Следует отметить, что одним из наиболее важных различий между unsigned char и char8_t являются требования к псевдонимам.unsigned char указатели могут быть псевдонимами практически всего остального.В отличие от char8_t указатели не могут.Таким образом, разумно ожидать (в зрелой реализации, а не в чем-то, что превосходит стандарт, который он реализует на рынке) разный код в разных случаях.Хитрость в том, что char8_t код должен быть более эффективным, если он отличается, поскольку компилятору больше не нужно выдавать код, который выполняет дополнительную работу для обработки потенциальных псевдонимов из хранилищ.

...