Является ли вложенный std :: transform неэффективным? - PullRequest
0 голосов
/ 17 апреля 2020

Если у меня есть std::string:

std::string s{"hello"};

и al oop, который изменяет его на месте, например так:

for (auto &c: s)
  c = std::toupper(c);

Я могу заменить его на эквивалент transform:

std::transform(s.begin(), s.end(), s.begin(),
               [](unsigned char c) -> unsigned char 
               { return std::toupper(c); });

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

Однако, если у меня есть std::vector<std::string>:

std::vector<std::string> v {"hello", "how", "are", "you"};

и я модифицирую его на месте следующим образом:

for (auto & s : v)
  for (auto &c: s)
    c = std::toupper(c);

эквивалентное преобразование должно быть:

std::transform(std::begin(v), std::end(v), std::begin(v),
  [](auto s) {
    std::transform(std::begin(s), std::end(s), std::begin(s), 
      [](unsigned char c) -> unsigned char { return std::toupper(c); });
    return s;
});

Однако версия transform генерирует вдвое больше сборка , а выполняет соответственно плохо , что меня удивляет.

Разве std::transform не является в этом случае абстракцией с нулевой стоимостью или я просто неправильно ее использую?

1 Ответ

2 голосов
/ 17 апреля 2020

Пройдите и верните все по ссылке. В противном случае вы делаете несколько копий строки. Обратите внимание на изменение: [](auto& s) -> std::string& {

std::transform(std::begin(v), std::end(v), std::begin(v),
  [](auto& s) -> std::string& {
    std::transform(std::begin(s), std::end(s), std::begin(s), 
      [](unsigned char c) -> unsigned char { return std::toupper(c); });
    return s;
});

Я добавил две новые функции быстрого доступа к вашей ссылке. Тот, который принимает входную строку как передачу по ссылке. И другой, который также возвращается по ссылке. То есть:

static void Transform2(benchmark::State& state) {
  // Code before the loop is not measured

  std::vector<std::string> v {"hello", "how", "are", "you"};
  for (auto _ : state) {
    std::transform(std::begin(v), std::end(v), std::begin(v),
    [](auto& s) {
      std::transform(std::begin(s), std::end(s), std::begin(s), 
        [](unsigned char c) -> unsigned char { return std::toupper(c); });
      return s;
    });

  }
}
BENCHMARK(Transform2);


static void Transform3(benchmark::State& state) {
  // Code before the loop is not measured

  std::vector<std::string> v {"hello", "how", "are", "you"};
  for (auto _ : state) {
    std::transform(std::begin(v), std::end(v), std::begin(v),
    [](auto& s) -> std::string& {
      std::transform(std::begin(s), std::end(s), std::begin(s), 
        [](unsigned char c) -> unsigned char { return std::toupper(c); });
      return s;
    });

  }
}
BENCHMARK(Transform3);

В зависимости от того, насколько мне повезло, когда я запускаю тест, Transform3 почти (а иногда и равен) по производительности в реализации теста InPlace.

enter image description here

...