C++17的std::variant是否比动态多态性慢?

3

我在跟随这篇博客,尝试将一段动态多态代码替换为使用std::variantstd::visit。但是我无法让std::variant+std::visit比虚拟结构实现更好地工作。它的速度慢了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

对于动态多态性,我有以下针对intfloat的内容。

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 *> &comps, size_t i, size_t j) {
  for (auto &&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> &comps, size_t i, size_t j) {
  for (auto &&a: comps) {
    if (!std::visit([&](const auto &comp) {
      return comp.comp(i, j);
    }, a)) {
      return false;
    }
  }
  return true;
}

我在这里进行了基准测试: https://quick-bench.com/q/u-cBjg4hyQjOs6fKem9XSdW7LMs

请问为什么使用std::variant + std::visit的方法比动态多态性的方法慢?我原本预计会相反! 我的方法和/或基准测试是否存在问题?


1
你关注的那个博客没有描述静态多态性。请尝试查看这篇博客:https://mropert.github.io/2017/11/30/polymorphic_ducks/ - Eljay
实现std::variant有两种方式,一种是使用动态分派,另一种是使用魔法。 - n. m.
2个回答

5
使用variant并不构成"静态多态性"。它仍然是动态多态性,因为编译器不知道实际上哪种类型将使用variant。因此,相关代码必须尝试在运行时确定variant存储的内容,并据此进行调用。
请注意,您链接的文章也没有称之为"静态多态性"。它强调这只是与虚函数不同形式的“运行时多态性”。

我明白了,谢谢你的澄清(我已经相应地更改了标题)。有更好的方法实现这个吗? - n1r44
1
@n1r44,所以您想要一个比所有已知的动态分派实现都更快的实现,是吗? - n. m.
@n1r44 确定了,只需手动编写自己的动态分派。将多态性下移,消除一两个间接层。然后进行数周的针对实际数据的分析和调整。 - Yakk - Adam Nevraumont

0
理论上,使用std::variant和std::visit实现动态多态性应该不会比基于虚函数的实现更慢。
在您的方法中,以下代码中使用的lambda函数
if (!std::visit([&](const auto &comp) {
  return comp.comp(i, j);
}, a)) {
  return false;
}

在 std::visit 函数中,反复复制 i 和 j 的指针是性能差异的主要原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接