Jimmy
Jimmy

Reputation: 65

Fortran and C Mixed Programming (Shared Memory)

I have an existing Fortran codebase I'm working with and it's quite large. I am no Fortran programmer so I know that I'm not doing everything correctly here.

I'm trying to create and initialize an array of 1.6 million integers. I cannot get this to initialize in Fortran (using ifort or gfort) as I either would have too many line continuations or too long of lines.

So naturally, I switched to C and wrote a function to just initialize an array and it compiles in seconds with no problem. Now I'm trying to link the two together properly. I created a small test case here to simplify things. Here are the three files I'm working with:

init.c

void c_init_()
{
  static const int f_init_g[1600000] =
  {
    3263, 322, 3261, 60, 32249, 32244, 3229, 23408, 252407, 25326,
    25805, 25723, 25562, 25787, 4549, 32248, 32244, 32243, 253207, 21806,
    ---------------------------------------------------------------------
    25805, 25723, 25562, 25787, 4549, 32248, 32244, 32243, 253207, 21806
  };
}

init_mod.f90

MODULE INIT_MOD

  USE, INTRINSIC :: ISO_C_BINDING
  IMPLICIT NONE
  SAVE

  TYPE :: INIT_TYPE
    INTEGER (C_INT), DIMENSION(1600000) :: INIT
  END TYPE INIT_TYPE
  TYPE (C_PTR), BIND(C,NAME="f_init_g") :: INIT_CP
  TYPE (INIT_TYPE), POINTER :: INIT_FP

END MODULE INIT_MOD

main.f90

  PROGRAM INIT

    USE INIT_MOD
    USE, INTRINSIC :: ISO_C_BINDING

    TYPE (INIT_TYPE) :: INIT_T
    CALL c_init()
    CALL C_F_POINTER(INIT_CP,INIT_FP)
    INIT_T = INIT_FP

  END PROGRAM INIT

I compile this using the following commands:

icc -c init.c
ifort -c init_mod.f90
ifort main.f90 init_mod.o init.o

I get a segmentation fault when running because INIT_CP points to nothing as far as I can tell. I know I'm not successfully getting INIT_CP to point at the array in my C function. So I'm trying to figure out how to do that.

I would like if someone has a suggestion on how to initialize this array natively in Fortran. My final option that I'll do is make a small initialization of this array in assembly and write a script to generate the assembly code to initialize this array myself (based off the assembly from the small initialization I can mimic the same thing for any size array). I'm not as excited to do that, but it may be the easiest and most reliable solution.

Most importantly I want other Fortran subroutines that use this array to see that it is static in shape and value so that appropriate inter procedural optimizations can be done.

Upvotes: 1

Views: 709

Answers (1)

IanH
IanH

Reputation: 21451

Fortran-C Interoperable variables must have external linkage. As suggested by others in the comments, move the C declaration to file scope and lose the static specifier.

There is no need for an intermediate C_PTR - a Fortran array variable is directly interoperable with the appropriate C array.

Reducing the size of the array slightly:

/* C File scope */
const int f_init_g[3] = { 101, 102, 103 };

! Fortran
MODULE m
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT
  IMPLICIT NONE
  INTEGER(C_INT), BIND(C, NAME='f_init_g') :: f_init_g(3)
END MODULE m

PROGRAM p
  USE m
  IMPLICIT NONE
  PRINT *, f_init_g(2)
END PROGRAM p

Note that the starting premise - that it is impossible to define or initialize such an array from within Fortran only - is false. The rules around constant expressions in Fortran permit reference to existing named constants, including named constants that are arrays. If you decide to persist with this madness, and assuming that the value of the initializer cannot be described by some sort of expression, consider:

INTEGER, PARAMETER :: first(10)  &
   = [ 3263,    322,     3261,    60,       32249,  &
       32244,   3229,    23408,   252407,   25326 ]
INTEGER, PARAMETER :: second(10)  &
   = [ 25805,   25723,   25562,   25787,    4549,  &
       32248,   32244,   32243,   253207,   21806]
...
INTEGER, PARAMETER :: f_init_g(1600000) = [ first, second, ... ]

You will probably need intermediate named constant arrays before the final array constructor.

In the immediate above, f_init_g is a named constant, which is very visible to the compiler, and more likely to result in the optimisations that you seek.

However, you may run into compiler complexity limits, that defeat this latter approach.

When f_init_g is a variable initialized by C, you are basically reliant on the inter-language and inter-procedural optimisation capabilities of your tool set - and if those capabilities even exist, I wouldn't expect much from them for this case. I expect that you aren't going to lose much performance wise, beyond the one-off time for IO, if you read the value of the array in from a file at runtime.

Upvotes: 5

Related Questions