代码之家  ›  专栏  ›  技术社区  ›  Alejandro Cámara

计算矩阵各元素指数的最有效方法

  •  3
  • Alejandro Cámara  · 技术社区  · 14 年前

    我正在从Matlab迁移到C+GSL,我想知道

    B[i][j] = exp(A[i][j])
    

    请注意,这与矩阵指数不同:

    B = exp(A)
    

    我刚刚找到了蛮力解决方案(几个for循环),但是 有没有更聪明的方法?

    编辑

    Drew Hall解决方案的结果

    所有结果都来自1024x1024 for(for) 在每次迭代中 double 赋值(复数)。 .

    • 当考虑{Row,Column}-主要模式来存储矩阵时的结果:
      • 223.22 ms,当以行主模式在内环中的列上循环时(情况2)。
      • gsl_matrix_complex_set

    案例1的源代码 :

    for(i=0; i<Nx; i++)
    {
        for(j=0; j<Ny; j++)
        {
            /* Operations to obtain c_value (including exponentiation) */
            matrix[2*(i*s_tda + j)] = GSL_REAL(c_value);
            matrix[2*(i*s_tda + j)+1] = GSL_IMAG(c_value);
        }
    }
    

    案例2的源代码

    for(i=0; i<Nx; i++)
    {
        for(j=0; j<Ny; j++)
        {
            /* Operations to obtain c_value (including exponentiation) */
            matrix->data[2*(j*s_tda + i)] = GSL_REAL(c_value);
            matrix->data[2*(j*s_tda + i)+1] = GSL_IMAG(c_value);
        }
    }
    

    案例3的源代码 :

    for(i=0; i<Nx; i++)
    {
        for(j=0; j<Ny; j++)
        {
            /* Operations to obtain c_value (including exponentiation) */
            gsl_matrix_complex_set(matrix, i, j, c_value);
        }
    }
    
    4 回复  |  直到 14 年前
        1
  •  5
  •   Drew Hall    14 年前

    无法避免迭代所有元素并调用 exp() 或同等标准。但是迭代有更快和更慢的方法。

    特别是,您的目标应该是最小化缓存未命中。找出数据是按行主循环还是列主循环的顺序存储的,并确保循环的排列方式是,内部循环在内存中连续存储的元素上进行迭代,而外部循环则大步前进到下一行(如果是行主循环)或列(如果是列主循环)。尽管这看起来微不足道,但它可以在性能上产生巨大的差异(取决于矩阵的大小)。

    N绑定)。您需要获得一个指向底层内存块的原始指针(即,一个双精度指针) 而不是一个双重**)来做这件事。

        2
  •  3
  •   Rafe Kettler    14 年前

    不,除非有什么奇怪的数学怪癖我没听说过,你几乎只需要用两个for循环遍历元素。

        3
  •  2
  •   Mike Dunlavey    14 年前

    exp 对于一组数字来说,真的没有捷径。你得叫它(Nx*Ny)次。如果一些矩阵元素很简单,比如0,或者有重复的元素,那么一些记忆可能会有所帮助。

    DGPADM . 它是用Fortran语言写的,但是你可以用 f2c 把它转换成C。 Here's the paper on it.

        4
  •  0
  •   Skizz    14 年前

    如果你受到内存带宽的限制,那么一旦你按顺序访问内存,你能做的就不多了。当按顺序提取数据时,CPU和内存工作得最好。随机访问会影响吞吐量,因为数据很可能必须从RAM中提取到缓存中。你可以试着加快速度。

    如果您受到CPU的限制,那么您可以选择更多的选项。使用SIMD是一种选择,正如手工编码浮点代码(C/C++编译器在FPU代码中不太好)有很多原因。如果这是我,并且内部循环中的代码允许这样做,我将有两个指针进入数组,一个在开始处,另一个在数组的4/5处。每次迭代,将使用第一个指针执行SIMD操作,使用第二个指针执行标量FPU操作,这样循环的每次迭代都会执行五个值。然后,我将SIMD指令与FPU指令交织,以减少延迟开销。这不应该影响您的缓存,因为(至少在奔腾上)MMU可以同时传输多达四个数据流(即,无需任何提示或特殊指令即可为您预取数据)。