我已经分配了一个大的双向量,假设有 100000 个元素。在我的代码中的某个时刻,我想将所有元素设置为一个常量、非零值。如果不对所有元素使用 for 循环,我该如何做到这一点? 如果有帮助的话,我也在使用 BLAS 包。
你可以使用
std::fill
(#include <algorithm>
):
std::fill(v.begin(), v.end(), 1);
这当然也只是一个循环..
'fill'从你说的是对的。
请注意,也可以构造一个充满指定值的向量:
std::vector<double> vec(100000, 3.14);
所以如果“在某个时候”的意思是“施工后立即”,请改为执行此操作。此外,这意味着您可以这样做:
std::vector<double>(100000, 3.14).swap(vec);
如果“在某个时候”意味着“在更改大小后立即”,并且您期望/希望重新分配向量(“期望”如果您使其大于其先前的容量,“希望”如果你让它变得更小并希望它被修剪以节省内存)。
如果你不想循环,你总是使用
memset()
。
也就是说,
memset(myarr, 5, arrsize);
,以便用所有 5 填充它。当心隐式转换为无符号字符。
概要
#include <string.h> void * memset(void *b, int c, size_t len);
描述
The memset() function writes len bytes of value c (converted to an unsigned char) to the byte string b.
如果向量很大,并且您需要它运行得很快并且您正在使用 gcc,那么:
块移动代码生成(memcpy) 块集 (memset) 被重写。 GCC 现在可以选择最佳算法 (循环,展开循环,指令与 rep 前缀或库调用)基于 正在复制的块的大小和 正在优化的 CPU。
不幸的是其他答案没有按照提示进行操作,OP 想要将数组的元素设置为零。 使用 BLAS 而不是更多惯用函数(例如
memset
或 fill
)可能有多种原因。
例如 BLAS 操作可以线程化。
此外,memset
和 fill
不提供开箱即用的 strided 操作。
乍一看,BLAS 库似乎没有提供这样的功能,但是,有两个选项:
xSCAL
(x
可以是 s
、d
、c
、z
对于不同的数字类型)。SCAL
进行比例运算 V <- a*V
。
对于 a = 0
它将元素设置为零(大部分)。
xCOPY
,并从堆栈内存中复制一个零。下面的完整代码。
两种方法都有其自身的问题,第一种策略依赖于任意浮点数 x 的任意
x*0.0 == 0.0
。
x == NAN
或 x == infinity
在技术上是不正确的(两种情况均有说明)。
也许 BLAS 可以以不符合 IEEE 的方式进行编译,实际上可以做到这一点。
无论如何,如果出于某种原因您知道原始值是常规数字,那么您可以使用它。
第二个更健壮,但依赖于 BLAS 接受零步幅值。 (BLAS 是在 70 年代用 Fortran 语言发明和编码的,当时还没有发明整数零。) 我知道的大多数实现都允许零增量,至少对于
xCOPY
。
它还需要从某处取出“第一个”零;在这个例子中只是在堆栈中创建。
(如果您要推广到 GPU BLAS (cuBLAS),那么您需要在 GPU 中分配这个零。)
所以,换句话说,您必须了解您的平台和可用的 BLAS。
#include<cstdint>
#include<iostream>
#include<limits>
extern "C" {
void sscal_(int32_t const& n, float const& a, float* x, int32_t const& incx);
void scopy_(int32_t const& n, float const* x, int32_t const& incx, float* y, int32_t const& incy);
}
void set_zero_1(int32_t n, float* x, int incx) {
sscal_(n, 0.0F, x, incx);
}
void fill_value(int32_t n, float* x, int incx, float const* value_ptr) {
scopy_(n, value_ptr, 0, x, incx);
}
void set_zero_2(int32_t n, float* x, int incx) {
float const value = 0.0F; // can also be allocated or be a global if necessary
fill_value(n, x, incx, &value);
}
int main() {
float X[12] = {
99.9, 0.0, 0.0,
std::numeric_limits<float>::quiet_NaN(), 0.0, 0.0,
std::numeric_limits<float>::infinity(), 0.0, 0.0,
99.9, 0.0, 0.0
};
//set_zero_1( 4, &X[0], 3); // this fails because NAN and INF
set_zero_2(/*num elements*/ 4, /*origin*/ &X[0], /*stride*/ 3);
for(int i = 0; i != 12; i += 3) std::cout << X[i] << std::endl; // prints zeros
}
这样使用。它将打印零。
$ c++ a.cpp -L/opt/intel/oneapi/mkl/2023.0.0/lib/intel64 -lmkl_rt
$ export LD_LIBRARY_PATH=/opt/intel/oneapi/mkl/2023.0.0/lib/intel64
$ ./a.out
0
0
0
0
奖金,也有这种方式,但它依赖于 BLAS 是顺序的,并且它不能并行化或矢量化任何操作来使其工作,所以它可能是最糟糕的选择。
void set_zero_3(int32_t n, float* x, int incx) {
*x = 0.0F; // set one element to zero, somehow. (see note above about memory that is not accessible from the CPU)
scopy_(n - 1, x, incx, x + incx, incx);
}