Как использование static влияет на скорость моего кода? - PullRequest
7 голосов
/ 25 мая 2020

Я решал онлайн-упражнение, и в какой-то момент мне нужно было удалить "" в начале и в конце строки. Это был мой код:

void static inline process_value(std::string &value) {
    if (value.back() !='>') {
        value = value.substr(1, value.size()-2);
    }
}

Вызывается из этого теста l oop:

static void UsingStatic(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
      std::string valor("\"Hola\"");
      process_valueS(valor);
    // Make sure the variable is not optimized away by compiler
    benchmark::DoNotOptimize(valor);
  }
}

Просто из любопытства я провел тест .

  • Компилятор: Clang-9.0
  • std: c ++ 20
  • optim: O3
  • STL: libstdc ++ (GNU)

Пока я занимался этим, я решил удалить static из process_value, в результате чего void inline process_value было в остальном то же самое. К моему удивлению, он оказался медленнее.

Я думал, что stati c означает только то, что функция предназначена только для файла. Но здесь говорит, что «stati c» означает, что функция должна быть встроена компилятором, если это возможно ». Но в том случае, когда я удалил stati c, думаю, результат не должен был измениться. Теперь я запутался, что еще делает stati c, кроме разделения функции на один .cpp, как это влияет на производительность?

Дизассемблирование в QuickBench показывает, что NoUsingStatic l oop на самом деле вызывает process_value вместо того, чтобы встраивать его, несмотря на то, что ключевое слово inline делает это законным для компилятора. Но UsingStatic выполняет встроенный вызов process_valueS. Эта разница в принятии решений компилятором, по-видимому, объясняет разницу в производительности, но почему clang решил не встраивать объявленную простую функцию void inline process_value(std::string &value){ ... }?


EDIT: из-за вопроса был закрыт, потому что это было недостаточно ясно, я удалил части, которые не имели отношения к вопросу. Но если мне не хватает информации, сообщите мне в комментариях

Ответы [ 2 ]

6 голосов
/ 27 мая 2020

Clang использует решение, основанное на стоимости, будет ли функция встроена или нет. На эту стоимость влияет многое. На него влияет static.

К счастью, clang имеет вывод, где мы можем это наблюдать. Обратите внимание на этот Godbolt ссылка :

void call();

inline void a() {
    call();
}

static inline void b() {
    call();
}

void foo() {
    a();
    b();
}

В этом небольшом примере a() и b() совпадают, за исключением того, что b() - это stati c .

Если вы наведете указатель мыши на вызовы a() или b() на Godbolt (в окне OptViewer), вы можете прочитать:

a(): cost = 0, порог = 487

b(): стоимость = -15000, порог = 487

(clang встроит вызов, если стоимость меньше порога.)

clang дал b() гораздо меньшую стоимость, потому что это stati c. Кажется, что clang только один раз даст снижение стоимости на -15000 для функции stati c. Если b() вызывается несколько раз, стоимость всех b() s будет равна нулю, кроме одного.

Вот числа для вашего случая, ссылка :

process_value(): стоимость = 400, порог = 325 -> он чуть выше порога, не будет встроен

process_valueS():: стоимость = -14600, порог = 325 -> ОК для встроенного

Таким образом, очевидно, что static может иметь большое влияние, если его вызвать только один раз. Это имеет смысл, потому что однократная встраивание функции stati c не увеличивает размер кода.

Совет: если вы хотите заставить clang встроить функцию, используйте для этого __attribute__((always_inline)).

0 голосов
/ 31 мая 2020

inline - это просто совет компилятору, который может или не может действительно встроить ваш конкретный код.

Что касается ключевого слова static, если оно применяется к глобальной переменной, то оно имеет файловую область (как вы упомянули), если вы компилируете свой код как отдельную единицу компиляции. Таким образом, вы даже можете иметь доступ к глобальным переменным stati c из других файлов, если вы скомпилируете их как одну единицу компиляции. Это означает, что на самом деле область действия глобальных переменных c - это не файл, а единица компиляции (которая может быть или не быть одним файлом).

Но, поскольку у вас есть глобальная статистика c функция, а не переменная, она доступна отовсюду как глобальная функция c.

РЕДАКТИРОВАТЬ: Как было предложено @Peter Cordes в комментариях ниже, это может быть настоящий беспорядок с inline и stati c одновременно, поэтому официальный do c (https://en.cppreference.com/w/cpp/language/inline) говорит, что переопределение встроенных функций (и переменных начиная с C ++ 17) должно быть нестатичным c.

...