Издержки временного std :: tuple в операторе init - PullRequest
0 голосов
/ 21 мая 2018

Я играю с std::tuple s в разных контекстах и ​​наткнулся на некоторые вещи, которые работают, но я не уверен, есть ли у них снижение производительности.

В C ++ 17 оператор init имеетбыл введен для операторов if и switch.Чтобы определить несколько переменных разных типов, вы можете использовать временный кортеж и структурированные привязки.

if(const auto& [x, y] = std::tuple{some_expensive_int_calculation(), get_string_message()}; x != 0 && y != "") {
    std::cout << "X is " << x << ", message equals " << y << std::endl;
}

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

EDIT : Обновлен пример, чтобы компилятор не мог тривиально оптимизировать кортеж и прояснить проблему.

1 Ответ

0 голосов
/ 21 мая 2018

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

Действительно, с gcc 7.3 -O2 следующая программа:

#include <tuple>
#include <iostream>

int main()
{
    if(const auto& [x, y] = std::tuple{0, "Test"}; x == 0) {
        std::cout << y << std::endl;
    }
}

становится:

main:
# stack-frame related code
  sub rsp, 8
# get address of "Test" string
  mov esi, OFFSET FLAT:.LC0
# get address of std::cout
  mov edi, OFFSET FLAT:std::cout
# call operator <<
  call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
# move returned ostream reference to rdi (arg1)
  mov rdi, rax
# and call std::endl
  call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
# return 0
  xor eax, eax
# clean up stack and return
  add rsp, 8
  ret

Обратите внимание, что нигде в этом выводе ассемблера не упоминается x.Также нет упоминания о кортеже или условной ветви.

Это допускается правилом "как будто".

http://en.cppreference.com/w/cpp/language/as_if

...