Answers:
Valarrays(值数组)旨在将Fortran的某些速度引入C ++。您不会创建指针的valarray,因此编译器可以对代码进行假设并更好地对其进行优化。(Fortran这么快的主要原因是没有指针类型,所以不能有指针别名。)
Valarrays还具有一些类,这些类使您可以以一种相当简单的方式对其进行切片,尽管该标准的这一部分可能需要更多的工作。调整它们的大小具有破坏性,并且缺少迭代器。
因此,如果使用的是数字,那么使用valarray并不是很重要。否则,矢量将更加方便。
valarray
是一种在错误的时间在错误的地方出生的孤儿。这是一种优化的尝试,特别是针对编写时用于重型数学的机器-尤其是矢量处理器(如Crays)。
对于矢量处理器,通常要做的是将单个操作应用于整个阵列,然后将下一个操作应用于整个阵列,依此类推,直到完成所需的所有操作。
但是,除非您要处理的数组很小,否则缓存的效果往往很差。在大多数现代计算机上,通常希望(在可能的范围内)加载数组的一部分,对数组进行所有操作,然后再移至数组的下一部分。
valarray
还应该消除混叠的可能性,这至少在理论上可以使编译器提高速度,因为它可以更自由地将值存储在寄存器中。但是,实际上,我完全不确定任何实际的实现都可以在很大程度上利用此优势。我怀疑这是一个鸡与蛋的问题-没有编译器支持,它就不会流行,而且只要它不流行,就不会有人麻烦他们的编译器来支持它。
还有一些令人困惑的(字面意义上的)辅助类数组与valarray一起使用。您将获得slice
和slice_array
,gslice
并gslice_array
使用的各个部分valarray
,并使它的作用类似于多维数组。您还可以mask_array
“屏蔽”操作(例如,将x中的项添加到y,但仅在z为非零的位置)。要简单地使用valarray
,您必须了解很多有关这些辅助类的知识,其中一些非常复杂,并且没有一个(至少在我看来)没有很好的文档记录。
最重要的是:尽管它有光彩的一刻,可以很好地完成某些事情,但也有一些很好的理由使它(几乎肯定会保留)晦涩难懂。
编辑(八年后的2017年):至少在某种程度上已经过时。例如,英特尔为其编译器实现了valarray的优化版本。它使用英特尔集成性能基元(Intel IPP)来提高性能。尽管确切的性能改进无疑会有所不同,但是与使用“标准”实现编译的相同代码相比,使用简单代码进行的快速测试显示出速度提高了约2:1 valarray
。
因此,尽管我并不完全相信C ++程序员将开始大量使用valarray
,但至少在某些情况下它可以提高速度。
在C ++ 98的标准化过程中,将valarray设计为允许进行某种快速的数学计算。但是,在那时,Todd Veldhuizen发明了表达模板并创建了blitz ++,并且发明了类似的模板元技术,这使得valarray在标准发布之前就已经过时了。IIRC是valarray的原始提议者,已将其放弃到标准化的一半,(如果为真)也无济于事。
ISTR,未将其从标准中删除的主要原因是,没有人花时间对问题进行彻底评估并撰写提案以将其删除。
但是请记住,所有这些都是模糊的记忆传闻。随便吃一点盐,希望有人能纠正或确认。
我知道valarrays有一些语法糖
我不得不说,我认为std::valarrays
语法糖的使用方式不多。语法不同,但是我不会称其为“糖”。该API很奇怪。《C ++编程语言》中关于std::valarray
s 的部分提到了这种不寻常的API,并且由于希望对s进行高度优化,因此在使用它们时收到的任何错误消息可能都是非直觉的。std::valarray
出于好奇,大约一年前,我std::valarray
反对了std::vector
。我不再需要代码或精确的结果了(尽管编写自己的代码并不难)。使用GCC 进行简单数学运算时,确实获得了一点性能好处std::valarray
,但是对于我的实现来计算标准偏差却没有(在数学上,标准偏差并不那么复杂)。 我怀疑在(注意,根据musiphil的建议,我设法从std::vector
缓存上,对每个项目的操作都比对std::valarray
s的操作更好。vector
和获得了几乎相同的性能valarray
)。
最后,我决定在使用std::vector
时要特别注意内存分配和临时对象创建之类的事情。
二者std::vector
并std::valarray
存储在一个连续的块中的数据。但是,他们使用不同的模式访问数据,更重要的是,与的API相比,用于的API std::valarray
鼓励使用不同的访问模式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;
我可能对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 lambda。
显然,这两个代码段做了不同的事情。首先,该std::vector
示例不会像该std::valarray
示例那样进行中间集合。但是,我认为比较它们是公平的,因为差异与std::vector
和之间的差异有关std::valarray
。
当我写这个答案时,我怀疑从两个std::valarray
s 中减去元素的值(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。
std::vector
缓存比a更好玩的任何原因std::valarray
;它们都为其元素分配单个连续的内存块。
valarray
上面的示例,您不必构造一个temp
valarray
对象,但是您可以完成此操作std::valarray<double> differences_from_mean = original_values - mean;
,然后缓存行为应类似于该vector
示例。(顺便说一句,如果mean
真的是int
,不是double
,您可能需要static_cast<double>(mean)
。)
valarray
。我需要查看是否可以提高性能。至于mean
存在int
:那是一个错误。我最初使用int
s 编写了示例,然后意识到mean
由于截断,因此与实际均值相差很远。但是我在第一轮编辑中错过了一些必要的更改。
我发现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
您可以像v1 = a*v2 + v3
开箱即用那样使用标准数学符号。除非定义了自己的运算符,否则使用向量是不可能的。