C语言中kronecker积的高效计算

C语言中kronecker积的高效计算,c,linear-algebra,C,Linear Algebra,我是C语言的新手,在我的大部分研究中都不需要比python更快的速度。然而,事实证明,我最近所做的工作需要计算相当大的向量/矩阵,因此C+MPI解决方案可能是合适的 从数学上讲,这项任务非常简单。我有很多维数为40k的向量,我希望计算这些向量的选定对的值,然后求这些kronecker积的和 问题是,如何有效地做到这一点?使用for循环或获得效果的以下代码结构是否有任何错误 下面描述的函数kron传递长度vector\u size的向量A和B,并计算其kronecker乘积,该乘积存储在C、Ave

我是C语言的新手,在我的大部分研究中都不需要比python更快的速度。然而,事实证明,我最近所做的工作需要计算相当大的向量/矩阵,因此C+MPI解决方案可能是合适的

从数学上讲,这项任务非常简单。我有很多维数为40k的向量,我希望计算这些向量的选定对的值,然后求这些kronecker积的和

问题是,如何有效地做到这一点?使用for循环或获得效果的以下代码结构是否有任何错误

下面描述的函数
kron
传递长度
vector\u size
的向量
A
B
,并计算其kronecker乘积,该乘积存储在
C
、A
vector\u size*vector\u size
矩阵中

void kron(int *A, int *B, int *C, int vector_size) {

    int i,j;

    for(i = 0; i < vector_size; i++) {
        for (j = 0; j < vector_size; j++) {
            C[i*vector_size+j] = A[i] * B[j];
        }
    }
    return;
}
void kron(int*A,int*B,int*C,int向量大小){
int i,j;
对于(i=0;i
这对我来说似乎很好,当然(如果我没有犯一些愚蠢的语法错误的话)会产生正确的结果,但我有一种隐秘的怀疑,即嵌入式for循环不是最优的。如果我还有别的办法,请告诉我。欢迎提出建议


我感谢你的耐心和你的建议。再一次,我对C非常缺乏经验,但谷歌搜索给我带来了一点乐趣。

对于双精度向量(单精度和复杂度相似),您可以使用BLAS例程
DGER
(排名一更新)或类似工具一次一个地完成产品,因为它们都是向量。你要乘多少向量?请记住,添加一组向量外积(您可以将Kronecker积视为)最终会得到矩阵乘法,BLAS的
DGEMM
可以有效地处理这一问题。但是,如果您确实需要整数运算,您可能需要编写自己的例程。

因为您的循环体都是完全独立的,所以肯定有一种方法可以加速这一过程。在考虑MPI之前,最简单的方法就是利用几个内核。OpenMP在这方面应该做得很好

#pragma omp parallel for
for(int i = 0; i < vector_size; i++) {
    for (int j = 0; j < vector_size; j++) {
        C[i][j] = A[i] * B[j];
    }
}
#pragma omp parallel for
对于(int i=0;i
现在许多编译器都支持这一点

您也可以尝试将一些常见的表达式从内部循环中拖出,但一些像样的编译器(如gcc、icc或clang)应该可以自己完成这项工作:

#pragma omp parallel for
for(int i = 0; i < vector_size; ++i) {
    int const x = A[i];
    int * vec = &C[i][0];
    for (int j = 0; j < vector_size; ++j) {
        vec[j] = x * B[j];
    }
}
#pragma omp parallel for
对于(int i=0;i

顺便说一句,使用
int
索引通常不是正确的做法
size\t
对于与索引和对象大小有关的所有内容来说都是正确的
typedef

这是数值计算界一个常见的问题,最好的办法是使用一个调试良好的包,比如(或它的一个)

你甚至可以找到它的a,这样你就可以摆脱C

所有这些(可能)都将比严格用python编写的代码快。如果你需要更高的速度,我建议你做几件事:

  • 研究使用Fortran而不是C。Fortran编译器往往更擅长优化数值计算(一个例外是如果您使用gcc,因为它的C和Fortran编译器都使用相同的后端)
  • 考虑并行化您的算法。我知道Fortran的一些变体有并行循环语句。我认为有一些C插件可以做同样的事情。如果你使用的是PC(和单精度),你也可以考虑使用你的视频卡的GPU,GPU本质上是一个非常便宜的阵列处理器。李>
    如果编译器支持C99(并且你永远不会传递与<代码> A/<代码>和<代码> b>代码>相同的向量,请考虑在C99支持模式下编译并将函数签名更改为:

    void kron(int * restrict A, int * restrict B, int * restrict C, int vector_size);
    

    restrict
    关键字向编译器保证由
    A
    B
    C
    指向的数组不会别名(重叠)。在编写代码时,编译器必须在每次执行内部循环时重新加载
    A[i]
    ,因为它必须是保守的,并且假定存储到
    C[]
    可以修改
    A[]
    中的值。在
    restrict
    下,编译器可以假设不会发生这种情况。

    找到了解决方案(感谢@Jeremiah Willcock):这似乎做得很好。如果我们逐步选择向量对
    A
    B
    ,并将它们添加到一些“运行总计”向量/矩阵
    C
    ,则上述kron函数的以下修改版本

    void kronadd(int *A, int *B, int *C, int vector_size, int alpha) {
    
        int i,j;
    
        for(i = 0; i < vector_size; i++) {
            for (j = 0; j < vector_size; j++) {
                C[i*vector_size+j] = alpha * A[i] * B[j];
            }
        }
        return;
    }
    
    void kronadd(int*A,int*B,int*C,int向量大小,int alpha){
    int i,j;
    对于(i=0;i
    从功能上讲,精确地对应于BLAS DGER函数(可作为访问)。初始的
    kron
    函数是DGER,其中
    alpha=0
    C
    是具有正确维度的未初始化(归零)矩阵/向量

    事实证明,最终简单地为这些库使用python绑定可能会更容易。然而,我想我在试图弄明白这件事的过程中学到了很多。在其他回答中有一些更有用的建议,如果你有同样的问题要处理,一定要查看它们。Tha
    int i, j;
    
    for(i = 0; i < vector_size; i++) {
        int d = *A++;
        int *e = B;
    
        for (j = 0; j < vector_size; j++) {
            *C++ = *e++ * d;
        }
    }