用非零值初始化双数组(BLAS)

问题描述 投票:0回答:4

我已经分配了一个大的双向量,假设有 100000 个元素。在我的代码中的某个时刻,我想将所有元素设置为一个常量、非零值。如果不对所有元素使用 for 循环,我该如何做到这一点? 如果有帮助的话,我也在使用 BLAS 包。

c++ c blas
4个回答
10
投票

你可以使用

std::fill
#include <algorithm>
):

std::fill(v.begin(), v.end(), 1);

这当然也只是一个循环..


4
投票

'fill'从你说的是对的。

请注意,也可以构造一个充满指定值的向量:

std::vector<double> vec(100000, 3.14);

所以如果“在某个时候”的意思是“施工后立即”,请改为执行此操作。此外,这意味着您可以这样做:

std::vector<double>(100000, 3.14).swap(vec);

如果“在某个时候”意味着“在更改大小后立即”,并且您期望/希望重新分配向量(“期望”如果您使其大于其先前的容量,“希望”如果你让它变得更小并希望它被修剪以节省内存)。


0
投票

如果你不想循环,你总是使用

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。


0
投票

不幸的是其他答案没有按照提示进行操作,OP 想要将数组的元素设置为零。 使用 BLAS 而不是更多惯用函数(例如

memset
fill
)可能有多种原因。 例如 BLAS 操作可以线程化。 此外,
memset
fill
不提供开箱即用的 strided 操作。

乍一看,BLAS 库似乎没有提供这样的功能,但是,有两个选项:

  1. 可以为此利用 BLAS 函数
    xSCAL
    x
    可以是
    s
    d
    c
    z
    对于不同的数字类型)。

SCAL
进行比例运算
V <- a*V
。 对于
a = 0
它将元素设置为零(大部分)。

  1. 使用
    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);
}
© www.soinside.com 2019 - 2024. All rights reserved.