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

有帮助吗?

解决方案

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

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

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

其他提示

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

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

然而,除非你处理的是相当小的数组,否则缓存效果会很差。在大多数现代机器上,你通常喜欢的(尽可能的)是加载数组的一部分,对它进行所有操作,然后转到数组的下一部分。 / p>

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

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

底线:虽然它有一些光彩,但可以做得非常整齐,但也有一些非常好的理由(并且几乎肯定会保留)模糊不清。

编辑(八年后,2017年):前面的一些已经至少在某种程度上已经过时了。例如,英特尔为其编译器实现了valarray的优化版本。它使用英特尔集成性能基元(英特尔IPP)来提高性能。虽然确切的性能改进无疑会有所不同,但与使用“标准”编译的相同代码相比,使用简单代码的快速测试显示速度提高了大约2:1。 valarray 的实现。

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

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

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

但是请记住,所有这些都是模糊地记住传闻。带着一点点盐,并希望有人纠正或证实这一点。

  

我知道valarray有一些语法糖

我不得不说,我不认为 std :: valarrays 在语法糖方面有很多。语法不同,但我不会将差异称为“糖”。 API很奇怪。 C ++编程语言 std :: valarray 的部分提到了这个不寻常的API,因为 std :: valarray 是预计将进行高度优化,使用它们时获得的任何错误消息都可能是非直观的。

出于好奇,大约一年前,我将 std :: valarray std :: vector 进行了对比。我不再拥有代码或精确的结果(尽管编写自己的代码并不难)。使用GCC I 在使用 std :: valarray 进行简单数学时获得了一点性能优势,但不是我的实现计算标准偏差(当然还有标准偏差)就数学而言,并不复杂。 我怀疑大型 std :: vector 中每个项目的操作对缓存的效果要好于 std :: valarray s上的操作。(< strong>注意,根据 musiphil 的建议,我设法从获得几乎相同的性能vector valarray )。

最后,我决定使用 std :: vector ,同时密切关注内存分配和临时对象创建等事项。


std :: vector std :: valarray 都将数据存储在一个连续的块中。但是,他们使用不同的模式访问该数据,更重要的是, std :: valarray 的API鼓励使用与 std :: vector 的API不同的访问模式。

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

对于 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;

我可能对 std :: slice std :: 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));

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

很明显,这两个代码片段做了不同的事情。例如, std :: vector 示例不会像 std :: valarray 示例那样创建一个中间集合。但是,我认为比较它们是公平的,因为差异与 std :: vector std :: valarray 之间的差异有关。

当我写这个答案时,我怀疑从两个 std :: valarray 中减去元素的值( 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;

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

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

Josuttis的书中包含了一些关于valarray的有趣(有点贬低)的评论(此处此处)。

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

我找到了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);

我们可以使用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;
}

另外,我们需要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()

C ++ 11标准说:

  

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

参见C ++ 11 26.6.1-2。

std :: valarray适用于繁重的数字任务,例如计算流体动力学或计算结构动力学,其中您拥有数百万,有时数以千万计的项目的数组,并且您还可以循环遍历它们数百万时间步也许今天std :: vector具有相似的性能,但是大约15年前,如果你想编写一个有效的数值求解器,valarray几乎是强制性的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top