C++ valarray与矢量

rlb*_*ond 152 c++ stl stdvector c++-standard-library valarray

我很喜欢矢量.他们很快,很快.但我知道这个叫做valarray的东西存在.为什么我会使用valarray而不是矢量?我知道valarray有一些语法糖,但除此之外,它们什么时候有用?

Jer*_*fin 145

valarray是一种在错误的时间出生在错误地方的孤儿.这是一次优化尝试,非常适合用于编写重型数学的机器 - 特别是像Crays这样的矢量处理器.

对于矢量处理器,您通常要做的是将单个操作应用于整个阵列,然后将下一个操作应用于整个阵列,依此类推,直到您完成了所有需要执行的操作.

但是,除非你正在处理相当小的数组,否则它往往在缓存方面效果不佳.在大多数现代机器上,你通常喜欢的(尽可能的)是加载数组的一部分,对它进行所有操作,然后转到数组的下一部分.

valarray也应该消除任何混叠的可能性,这至少在理论上可以让编译器提高速度,因为它可以更自由地将值存储在寄存器中.但实际上,我并不确定任何真正的实现都会在很大程度上利用这一点.我怀疑这是一个鸡蛋和鸡蛋的问题 - 没有编译器支持它没有变得流行,只要它不受欢迎,没有人会麻烦他们的编译器支持它.

还有一个与valarray一起使用的令人眼花缭乱的(字面上)辅助类数组.你得到切片,slice_array,Gslice上和gslice_array与valarray中的碎片玩耍,并使其像一个多维数组.您还可以使用mask_array"屏蔽"操作(例如,将项目添加到x到y,但仅限于z非零的位置).为了更多地使用valarray,你必须学习很多关于这些辅助类的知识,其中一些非常复杂,而且似乎(至少对我来说)没有一个很好的记录.

一句话:虽然它有一点点光彩,并且可以做一些非常整洁的事情,但也有一些很好的理由它(并且几乎肯定会保持)模糊不清.

编辑(八年后,2017年):前面的一些已经至少在某种程度上已经过时了.例如,英特尔为其编译器实现了valarray的优化版本.它使用英特尔集成性能基元(英特尔IPP)来提高性能.虽然确切的性能改进无疑而变化,快速测试以简单的代码示出了围绕2:在速度1的改进,与用"标准"的执行编译的代码相同valarray.

因此,虽然我并不完全相信C++程序员将开始大量使用valarray,但在某些情况下它可以提供速度提升.

  • @Mehrdad:是的 - 有在[Numeric.Requirements]的限制(相当长)列表.对于只是一对夫妇的例子,所有的抽象类和异常被禁止.它还需要(例如)复制构造和随后分配默认构造的序列之间的等价. (6认同)
  • 我不会基于恐惧来决定.我根据您是否需要存储使用它禁止的功能的元素来决定它. (4认同)
  • @annoying_squid:如果您要添加更具体且(您相信)准确的信息,请随时添加显示该信息的答案。就目前而言,您的评论似乎并未添加任何有用的信息。 (2认同)

Tim*_*man 67

Valarrays(值数组)旨在将Fortran的一些速度带入C++.你不会做一个指针的变量,所以编译器可以对代码做出假设并更好地优化它.(Fortran速度如此之快的主要原因是没有指针类型,因此不存在指针别名.)

Valarrays还有一些类,允许您以一种相当简单的方式对它们进行切片,尽管该部分标准可以使用更多的工作.调整它们的大小是破坏性的,它们缺乏迭代器.

所以,如果它是你正在使用的数字,便利并不是那么重要的使用valarrays.否则,向量会更方便.

  • 自从Fortran 90开始,Fortran就有了一些指针. (15认同)
  • 它们不是为避免指针而设计的.C++ 11在valarray中定义了begin()和end(),它们将迭代器返回给它们 (10认同)
  • @ user2023370:这就是为什么有这么多Fortran用户更喜欢Fortran 77. :) (3认同)

sbi*_*sbi 38

在C++ 98的标准化过程中,valarray被设计为允许某种快速的数学计算.然而,在那个时候,Todd Veldhuizen发明了表达模板并创建了闪电战++,并且发明了类似的模板元技术,这使得valarray在标准发布之前几乎已经过时了.IIRC,valarray的原始提议者放弃了标准化的一半,(如果是真的)也没有帮助它.

ISTR认为没有从标准中删除的主要原因是没有人花时间彻底评估问题,并提出删除它的建议.

但请记住,所有这一切都隐约记得传闻.带上一粒盐,希望有人纠正或证实这一点.

  • 《C++ 模板 - 完整指南》一书中提到了这一点,我认为人们普遍认为他们都是[独立]发明了它们(http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/网站/文章/CUJ/2003/0306/cuj0306becker/)。 (2认同)

Max*_*ert 27

我知道valarray有一些语法糖

我不得不说,我认为std::valarrays没有太多的语法糖.语法不同,但我不会把差异称为"糖".API很奇怪." C++编程语言"中有关std::valarrays 的部分提到了这个不寻常的API以及这样一个事实:由于s预计会被高度优化,因此使用它们时获得的任何错误消息都可能是非直观的.std::valarray

出于好奇,大约一年前我std::valarray反对std::vector.我不再拥有代码或精确的结果(尽管编写自己的代码并不难).使用GCC我在使用简单数学时确实获得了一些性能优势std::valarray,但不是我的实现计算标准偏差(当然,就数学而言,标准偏差并不复杂). 我怀疑大型std::vector游戏中每个项目的操作对缓存比对std::valarrays的操作更好.(注意,从下面的建议musiphil,我已经成功地得到几乎相同的性能vectorvalarray).

最后,我决定在std::vector关注内存分配和临时对象创建等事情时使用.


std::vectorstd::valarray将数据存储在一个连续的块.但是,他们使用不同的模式访问这些数据,更重要的是,API用于std::valarray鼓励不同的API访问模式std::vector.

对于标准偏差示例,在特定步骤中,我需要找到集合的均值以及每个元素的值与均值之间的差异.

对于std::valarray,我做了类似的事情:

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> temp(mean, original_values.size());
std::valarray<double> differences_from_mean = original_values - temp;
Run Code Online (Sandbox Code Playgroud)

我可能已经有更聪明std::slicestd::gslice.现在已经五年多了.

因为std::vector,我做了一些事情:

std::vector<double> original_values = ... // obviously, I put something here
double mean = std::accumulate(original_values.begin(), original_values.end(), 0.0) / original_values.size();

std::vector<double> differences_from_mean;
differences_from_mean.reserve(original_values.size());
std::transform(original_values.begin(), original_values.end(), std::back_inserter(differences_from_mean), std::bind1st(std::minus<double>(), mean));
Run Code Online (Sandbox Code Playgroud)

今天我肯定会以不同的方式写出来.如果不出意外,我会利用C++ 11 lambdas.

很明显,这两段代码做了不同的事情.例如,该std::vector示例不像示例那样制作中间集合std::valarray.但是,我认为这是公平的,因为差异绑之间的差异对它们进行比较std::vectorstd::valarray.

当我写这个答案时,我怀疑从两个std::valarrays 中减去元素的值(std::valarray示例中的最后一行)将比示例中的相应行std::vector(这也恰好是最后一行)的缓存友好性更低.

然而,结果证明了这一点

std::valarray<double> original_values = ... // obviously I put something here
double mean = original_values.sum() / original_values.size();
std::valarray<double> differences_from_mean = original_values - mean;
Run Code Online (Sandbox Code Playgroud)

std::vector示例相同,并且具有几乎相同的性能.最后,问题是您喜欢哪种API.


tim*_*day 23

valarray应该让一些FORTRAN矢量处理的好处在C++上消失.不知何故,必要的编译器支持从未发生过.

Josuttis的书中包含了一些关于valarray(这里这里)的有趣(有点贬低)的评论.

但是,英特尔现在似乎在他们最近的编译器版本中重新审视了valarray(例如见幻灯片9); 这是一个有趣的发展,因为他们的4路SIMD SSE指令集即将加入8路AVX和16路Larrabee指令,为了便于携带,用类似抽象编码可能要好得多. valarray比(说)内在论.


Zet*_*eta 13

我找到了valarray的一个很好的用法.它就像numpy数组一样使用valarray.

auto x = linspace(0, 2 * 3.14, 100);
plot(x, sin(x) + sin(3.f * x) / 3.f + sin(5.f * x) / 5.f);
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我们可以用valarray实现上面的内容.

valarray<float> linspace(float start, float stop, int size)
{
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + i * (stop-start)/size;
    return v;
}

std::valarray<float> arange(float start, float step, float stop)
{
    int size = (stop - start) / step;
    valarray<float> v(size);
    for(int i=0; i<size; i++) v[i] = start + step * i;
    return v;
}

string psstm(string command)
{//return system call output as string
    string s;
    char tmp[1000];
    FILE* f = popen(command.c_str(), "r");
    while(fgets(tmp, sizeof(tmp), f)) s += tmp;
    pclose(f);
    return s;
}

string plot(const valarray<float>& x, const valarray<float>& y)
{
    int sz = x.size();
    assert(sz == y.size());
    int bytes = sz * sizeof(float) * 2;
    const char* name = "plot1";
    int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, bytes);
    float* ptr = (float*)mmap(0, bytes, PROT_WRITE, MAP_SHARED, shm_fd, 0);
    for(int i=0; i<sz; i++) {
        *ptr++ = x[i];
        *ptr++ = y[i];
    }

    string command = "python plot.py ";
    string s = psstm(command + to_string(sz));
    shm_unlink(name);
    return s;
}
Run Code Online (Sandbox Code Playgroud)

另外,我们需要python脚本.

import sys, posix_ipc, os, struct
import matplotlib.pyplot as plt

sz = int(sys.argv[1])
f = posix_ipc.SharedMemory("plot1")
x = [0] * sz
y = [0] * sz
for i in range(sz):
    x[i], y[i] = struct.unpack('ff', os.read(f.fd, 8))
os.close(f.fd)
plt.plot(x, y)
plt.show()
Run Code Online (Sandbox Code Playgroud)

  • 当我今天在工作中发现 valarray 时,我的想法与您完全相同。我认为从现在开始,对于 C++ 中的数学处理问题,我将使用 valarray,因为从数学的角度来看,代码看起来更容易理解。 (2认同)

Lin*_*gxi 8

C++ 11标准说:

valarray数组类被定义为没有某些形式的别名,因此允许对这些类的操作进行优化.

见C++ 11 26.6.1-2.


Pau*_*zak 5

您可以像开箱即用一样std::valarray使用标准数学符号。v1 = a*v2 + v3这对于向量来说是不可能的,除非您定义自己的运算符。