在fortran

时间:2015-06-12 09:18:44

标签: arrays fortran slice blas

LDx BLAS函数的各种?gemm参数可以在更大的数组切片上运行。例如,这个小C程序对(200,200)矩阵的左上角和右上角(100,100)子矩阵进行矩阵乘法,并将结果存储在左下角(100,100)子矩阵中。

#include <stdlib.h>
#include <stdio.h>
#include <cblas.h>
int main()
{
    double * a = malloc(200*200*sizeof(double));
    for(int i = 0; i < 200*200; i++) a[i] = 1;
    cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 100, 100, 100, 1, a, 200, a+100*200, 200, 0, a+100, 200);
    printf("%f\n", a[100]);
    return 0;
}

$ gcc -o cblas_test{,.c} -lcblas
$ ./cblas_test
100.000000

正如预期的那样,输出为100。由于BLAS最初是一个fortran库,我希望这也是可能的。但我无法看到如何指定那里的数组偏移量。使用切片不起作用,导致以下程序出现分段错误:

program fblas_example
    implicit none
    real(8), allocatable :: a(:,:)
    allocate(a(200,200))
    a = 1
    call dgemm('n','n',100,100,100,1d0,a,200,a(1:100,101:200),200,0d0,a(101:200,1:100),200)
    write(*,*) a(101,1)
end program

$ gfortran -o fblas_example{,.f90} -lblas
$ ./fblas_example
*** Error in `./fblas_example': double free or corruption (out): 0x00000000011351c0 ***

我在这里做错了什么?

编辑:如果我先将a按到1d数组中,这是有效的:

program fblas_example2
    use, intrinsic :: iso_c_binding
    implicit none
    real(8), target, allocatable :: a(:,:)
    real(8), pointer :: fa(:)
    allocate(a(200,200))
    a = 1
    call c_f_pointer(c_loc(a), fa, [size(a)])
    call dgemm('n','n',100,100,100,1d0,fa,200,fa(100*200+1:),200,0d0,fa(101:),200)
    write(*,*) fa(101)
end program

$ gfortran -o fblas_example2{,.f90} -lblas
$ ./fblas_example2
   100.00000000000000

但是很奇怪,人们必须通过ISO C绑定才能从fortran调用fortran库。

只是要清楚:我知道可以复制出来的块,对它们进行处理,然后将它们复制回去。但我在这里要问的是如何使用BLAS自己的支持来处理数组的子集。

2 个答案:

答案 0 :(得分:2)

当您提供LDx时,您声明矩阵的实际大小(前导维度)。然后BLAS使用此值跳过乘法中未使用的数据。

我认为您应该使用dgemmC相同的方式。我的意思是你不应该传递子矩阵a(1:100,101:200),而应该传递子矩阵的第一个值a(1,101)的位置。

我测试了以下内容,似乎工作正常。

program fblas_example
  implicit none
  real(8), allocatable :: a(:,:)
  allocate(a(200,200))
  a = 1
  call dgemm('n','n',100,100,100,1d0,a,200,a(1,101),200,0d0,a(101,1),200)
  write(*,*) a(101,1)
end program

$ gfortran dgemm.f -lblas
$ ./a.out 
  100.00000000000000   

答案 1 :(得分:2)

如果要传递Fortran&gt; = 90中的数组切片(而不是某些数组元素的地址),我认为Fortran代码中的以下行

call dgemm('n','n', 100,100,100, 1d0, a,200, a(1:100,101:200),200, 0d0, a(101:200,1:100),200 )

应该修改为

call dgemm('n','n', 100,100,100, 1d0, a,200, a(1:100,101:200),100, 0d0, a(101:200,1:100),100 )

后两个数组切片(或子数组)的前导维度已从200更改为100.

更具体地说,a(1:100,101:200)a(101:200,1:100)使用隐式接口传递给dgemm,其中数组切片在内存中是不连续的。在这种情况下,编译器将首先为每个子数组准备两个大小为100x100的数组临时数,将原始数组a的矩阵元素复制到数组临时数,并传递临时数的第一个元素的地址。完成dgemm的计算后,临时数据的内容将被适当地复制回原始数组a。如果发生这种情况,dgemm将收到两个前导维度为100(而不是200)的数组。

可以通过-fcheck-array-temporaries(在gfortran中)和-check arg_temp_created(在ifort中)选项来检查阵列临时值的生成。例如,后者给出了

forrtl: warning (402): fort: (1): In call to DGEMM, an array temporary was created for argument #9
forrtl: warning (402): fort: (1): In call to DGEMM, an array temporary was created for argument #12
100.000000000000