假设我有一个类似于以下的类:
struct Potato {
Eigen::Vector3d position;
double weight, size;
string name;
};
以及Potato
s
std::vector<Potato> potato_farm = {potato1, potato2, ...};
这显然是一种结构阵列(AoS)布局,因为,就大多数情况而言,将所有Potato
的数据集中在一起是有意义的。但是,我可能想要计算一个最常见的名称,其中数组结构(SoA)设计使事物与具有名称的事物类型无关(一组人都有名字,一组地方,所有的名字等等。)C ++是否有任何工具或技巧使AoS布局看起来像SoA一样做这样的事情,还是有更好的设计来完成同样的事情?
答案 0 :(得分:4)
您可以使用lambdas访问在范围内工作的algos中的特定成员:
double mean = std::accumulate( potato_farm.begin(), potato_farm.end(), 0.0, []( double val, const Potato &p ) { return val + p.weight; } ) / potato_farm.size();
如果这还不够,你不能让它看起来像数据数组,因为它需要对象在连续的内存中,但你可以使它像一个容器。因此,您可以实现自定义迭代器(例如type == double
的随机访问迭代器,它迭代权重成员)。如何实现自定义迭代器的描述是here。你甚至可以制作那种通用的,但目前尚不清楚这是否会使工作更糟糕,因为它不是很容易实现。
答案 1 :(得分:1)
不幸的是,没有语言工具可以将结构一般性地更改为SoA。当你试图将SIMD编程提升到更高水平时,这实际上是一个很大的障碍。
您需要手动创建SoA。但是,您可以通过创建对SoA对象的引用来帮助自己,就像它是一个普通的Potato一样。
struct Potato {
float position;
double weight, size;
std::string name;
};
struct PotatoSoARef {
float& position;
double& weight;
double& size;
std::string& name;
};
class PotatoSoA {
private:
float* position;
double* weight;
double* size;
std::string* name;
public:
PotatoSoA(std::size_t size) { /* allocate the SoA */ }
PotatoSoARef operator[](std::size_t idx) {
return PotatoSoARef{position[idx], weight[idx], size[idx], name[idx]};
}
};
这样,无论您是否拥有Potatos的AoS或SoA,您都可以将其字段作为arr[idx].position
等访问(均为r值和l值)。编译器可能会优化代理。
您可能还想添加其他构造函数和访问器。
如果您希望函数具有针对AoS和SoA访问模式的统一接口,您可能还有兴趣实现常规AoS,operator[]
返回PotatoSoARef
。
如果您愿意偏离C ++,您可能会对语言扩展感兴趣,例如Sierra
答案 2 :(得分:0)
正如Slava所说,如果不编写自己的迭代器,你就不会从AoS数据中获得类似SoA的访问权限,而且我认为真的很难确定STL算法的使用是否是那样的在这之前很重要,特别是如果这不是一般的解决方案。无论如何,SoA数据的主要好处是缓存性能,而不是您正在使用的任何容器的特定语法,除了实际的SoA数据之外什么都不会让您获得。
答案 3 :(得分:0)
使用range-v3(不在C ++ 17中: - /),您可以使用投影或转换视图:
ranges::accumulate(potato_farm, 0., ranges::v3::plus{}, &Potato::weight);
或
auto weightsView = potato_farm | ranges::view::transform([](auto& p) { return p.weight; });
ranges::accumulate(weightsView, 0.);