C 17 std::вариант медленнее, чем динамический полиморфизм?

#c #polymorphism #c 17 #std-variant #static-polymorphism

Вопрос:

Я следил за этим блогом и пытался заменить код динамического полиморфизма на использование std::variant и std::visit . Но я не могу заставить std::variant std::visit работать лучше, чем виртуальная структура impl. Это примерно в 1,3-1,5 раза медленнее! (GCC 10.3 -O3 C 17)

Вариант использования выглядит следующим образом. Допустим, мы сравниваем i-ю и j-ю строки двух таблиц. Таблица может содержать разнотипные столбцы. Предположим, что мы можем получить доступ к буферам столбцов. То, что я делаю, — это тестирование,

 def IndexEqual(Table:A, Table:B, int:i, int:j):
  for c in range(A.num_cols):
     if not A.column(c)[i] == B.column(c)[j]:
         return False
  return True

 

для динамического полиморфизма у меня есть следующее для int и float

 struct Comp{
  virtual bool comp(size_t i, size_t j) const = 0;
};

struct CompI: public Comp {
  CompI(const int *data_1, const int *data_2) : data1(data_1), data2(data_2) {}

  const int *data1, *data2;
  bool comp(size_t i, size_t j) const override {
    return data1[i] == data2[j];
  }
};

struct CompF: public Comp {
  CompF(const float *data_1, const float *data_2) : data1(data_1), data2(data_2) {}

  const float *data1, *data2;
  bool comp(size_t i, size_t j) const override {
    return data1[i] == data2[j];
  }
};

bool IndexEqual1(const std::vector<Comp *> amp;comps, size_t i, size_t j) {
  for (auto amp;amp;a: comps) {
    if (!a->comp(i, j)) {
      return false;
    }
  }
  return true;
}
 

Это было преобразовано в std::variant std::visit следующим образом.

 struct EqualToI {
  EqualToI(const int *data_1, const int *data_2) : data1(data_1), data2(data_2) {}
  const int *data1, *data2;
  bool comp(size_t i, size_t j) const {
    return data1[i] == data2[j];
  }
};

struct EqualToF {
  EqualToF(const float *data_1, const float *data_2) : data1(data_1), data2(data_2) {}
  const float *data1, *data2;
  bool comp(size_t i, size_t j) const {
    return data1[i] == data2[j];
  }
};

using var_type = typename std::variant<EqualToI, EqualToF>;

bool IndexEqual(const std::vector<var_type> amp;comps, size_t i, size_t j) {
  for (auto amp;amp;a: comps) {
    if (!std::visit([amp;](const auto amp;comp) {
      return comp.comp(i, j);
    }, a)) {
      return false;
    }
  }
  return true;
}
 

Я сравнил это здесь
https://quick-bench.com/q/u-cBjg4hyQjOs6fKem9XSdW7LMs

Может ли кто-нибудь объяснить, пожалуйста, почему этот std::variant std::visit вариант работает медленнее, чем подход динамического полиморфизма? Я ожидал иного! Есть ли проблема в моем подходе и/или эталоне?

Комментарии:

1. Этот блог, за которым вы следите, не описывает статический полиморфизм. Попробуйте вместо этого этот блог: mropert.github.io/2017/11/30/polymorphic_ducks

2. Существует два способа реализации std::вариант, с динамической отправкой и с помощью магии.

Ответ №1:

Использование variant не является «статическим полиморфизмом». Это все еще динамический полиморфизм, потому что компилятор не знает, какой тип на самом деле будет населять variant . Следовательно, рассматриваемый код должен попытаться выяснить, что variant хранится во время выполнения, и он должен соответствующим образом отправлять эти вызовы.

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

Комментарии:

1. Я понимаю. Спасибо за разъяснение (я соответственно изменил название). Есть ли лучший способ, которым я мог бы этого достичь?

2. @n1r44 Итак, вы хотите, чтобы реализация динамической отправки была быстрее, чем все известные, верно?

3. @n1r44, конечно, просто передайте свою собственную динамическую отправку. Переместите полиморфизм вниз, удалив один или два уровня косвенности. Затем недели профилирования и настройки на основе реалистичных данных.