#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, конечно, просто передайте свою собственную динамическую отправку. Переместите полиморфизм вниз, удалив один или два уровня косвенности. Затем недели профилирования и настройки на основе реалистичных данных.