雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

Fortran で巨大配列の計算を行うときの注意

概要

  • Fortran で巨大配列の計算を行うと、セグメンテーション違反することがある。
  • マシンの stack size 制限に注意。

情報

  • 実行環境
    • CentOS 6.4
    • fortran 90
    • fortran のソースコードは Cから呼び出し
  • lapack は yum でインストールしたもの。普通に使うには古いかも。。
$ yum install lapack lapack-devel

実行

  • C で、任意サイズの行列を作成し、fortran で計算させる。ここでは SVD。
$ ./a.out
Matrix size = ( 5, 6 )
Malloc memory.
Calculate SVD ...

  • 巨大な配列になると、セグメンテーション違反する!!
$ ./a.out 1000 1000
Matrix size = ( 1000, 1000 )
Malloc memory.
セグメンテーション違反です (コアダンプ)

  • fortran のメモリ領域は、マシンの stack size 分しか取らないらしい。
    • デフォルト ( 10240 ) のまま fortran のプログラムを動かしたのが原因!!
  • ulimit コマンドを使って、マシンの stack size を調整すると正常に動く。
$ ulimit -s unlimited
$ ./a.out 1000 1000
Matrix size = ( 1000, 1000 )
Malloc memory.
Calculate SVD ...

サンプルソース

  • main.c
#include <stdio.h>
#include <stdlib.h>

#define min(x,y) (((x) < (y)) ? (x) : (y))
#define max(x,y) (((x) < (y)) ? (y) : (x))

/**
 * Calculate SVD
 * matrix SRC, matrix U, array s, matrix Vt, matrix row, matrix col
 */
extern void svd_( double *, double *, double *, double *, int *, int * );

/**
 * Main
 */
int main( int argc, char *argv[] )
{
    int row = 5, col = 6;

    if ( argc >= 2 ) {
        row = atoi( argv[1] );
    }
    if ( argc >= 3 ) {
        col = atoi( argv[2] );
    }
    printf( "Matrix size = ( %ld, %ld )\n", row, col );

    size_t s_size = max( min( row, col ), 1 );
    size_t x, y;

    printf( "Malloc memory.\n" );
    double Src[ row * col ], U[ row * row ], s[ s_size ], Vt[ col * col ];

    int random_seed = 1;
    int threshold   = 60;
    for ( y = 0; y < row; y++ ) {
        for ( x = 0; x < col; x++ ) {
            Src[ x + y * col ] = ( rand() % 100 < threshold ) ? 0 : 1;
        }
    }

    printf( "Calculate SVD ... \n" );
    svd_( Src, U, s, Vt, &row, &col );

    return 0;
}

  • svd.f90
subroutine svd( src, u, s, vt, row, col )
  implicit none

  integer row, col
  double precision src( row * col )
  double precision u( row * row ), s( max( row, col ) ), vt( col * col )

  double precision w_src( row, col )
  double precision w_u( row, row ), w_vt( col, col )

  integer lwork
  integer lda, ldu, ldvt

  double precision :: work( max( 3 * min( row, col ) + max( row, col ), 5 * min( row, col ) ) )

  integer i, j, info

  lwork = max( 3 * min( row, col ) + max( row, col ), 5 * min( row, col ) )
  lda   = max( row,   1 )
  ldu   = max( row,   1 )
  ldvt  = max(   1, col )

  ! Array => Matrix
  do i = 1, row
     do j = 1, col
        w_src( i, j ) = src( j + ( i - 1 ) * col )
     end do
  end do

  ! SVD
  call dgesvd( 'S', 'S', row, col, w_src, lda, s, w_u, ldu, w_vt, ldvt, work, lwork, info )

  ! Matrix => Array
  do i = 1, row
     do j = 1, row
        u( j + ( i - 1 ) * row ) = w_u( i, j )
     end do
  end do

  do i = 1, col
     do j = 1, col
        vt( j + ( i - 1 ) * col ) = w_vt( i, j )
     end do
  end do

  return
end subroutine svd

  • Makefile
CC  = gcc
FTN = gfortran

a.out : svd.o main.o
	${FTN} -I /usr/local/lib64 -llapack $^

svd.o : svd.f90
	${FTN} -c $<

main.o : main.c
	${CC} -c $<


clean :
	rm -rf *.out
	rm -rf *.o