1

Suppose I have the following Fortran subroutine:

subroutine f_test(n,x,value)

    use iso_c_binding

    implicit none

    integer,      intent(in)  :: n
    real(kind=8), intent(in)  :: x(n)
    real(kind=8), intent(out) :: value

    integer(kind=c_int) :: n_
    real(kind=c_double) :: value_,x_(n)

    n_     = transfer( n, n_ )
    x(1:n) = transfer( x(1:n), x_(1:n) )

    call c_test(n_,x_,value_)

    value = transfer( value_, value )

end subroutine f_test

and also suppose that c_test is a pointer to a C routine that works over the vector x and returns a result in value variable.

This works perfectly, but I have a question about it:

The conversion of the vector x can dramatically affect the complexity of an algorithm that calls the subroutine f_test. I mean that, if a such algorithm calls f_test n times, the complexity of the algorithm will be quadratic, but without this conversion, it would be simply linear. So, this conversion made in this way is impracticable. Is there any reasonable way to work around this issue?

francescalus
  • 30,576
  • 16
  • 61
  • 96
johncg
  • 35
  • 3
  • What exactly does c_test do that it's O(1) complexity? – Jonathan Dursi Jul 06 '13 at 16:34
  • Actually, the complexity of `c_test` is not supposed to be O(1). Sorry, I didn't express correctly. The concern is not with the complexity of `f_test`, but with the call `x(1:n) = transfer( x(1:n), x_(1:n) )`. This is supposed to consume O(n) time, since I am copying a vector of size `n`. What I meant is that without this conversion, I would have no extra time consumption. – johncg Jul 08 '13 at 12:39
  • Right, but if the complexity of c_test is also O(n), I just don't see the concern here, unless the work done in c_test is less than that of a (possibly converting) assignment, which strikes me as unlikely. And even if that is the case, if c_test is that trivial it's probably easy to re-implement in fortran. – Jonathan Dursi Jul 08 '13 at 13:43

2 Answers2

3

The Fortran intrinsic transfer doesn't convert numeric types, it just copies bits, bypassing types. No conversion is taking place. If integer and integer (c_int) should be different types, or real (kind=8) and real (c_double) different types, a conversion would be necessary but will not be done by transfer. An assignment statement or an intrinsic function such as int or real could effect a conversion.

I assume that you use real (kind=8) to mean an 8-byte real. There is no guarantee that the kind value of 8 corresponds to that type. This has been discussed several times on Stackoverflow, e.g., Fortran: integer*4 vs integer(4) vs integer(kind=4)

The type integer without qualification and integer (c_int) might be the same, in which case no conversion is necessary. Similar for the others ... in which case the compiler will likely accept call c_test(n,x,value), recognizing that the types are identical, since the numeric values of the kinds are the same, even though different symbols were used. To be more portable, you could the int and real intrinsic function for the input arguments and use an assignment for the output:

call c_test ( int (n, c_int), real (x, c_double), value_)
value = value_

If you want to be sure that no conversions take place, you could use integer (c_int), real (c_double), etc., throughout you Fortran code. This would also give you a portable way of specifying an 8-byte real. (Which can also be done by using ISO_FORTRAN_ENV.)

Community
  • 1
  • 1
M. S. B.
  • 28,968
  • 2
  • 46
  • 73
  • OK, thanks for your answer! My main concern is the case when `c_double` is not `8`. In these cases, it would be required to make the type conversion, and by calling `real(x, c_double)`, I am affraid to dramatically change the complexity of my algorithm (since this conversion seems to have linear time consumption in `n`, instead of a constant one). Do you think that is there a way to convert `x` in constant time (someway working with pointers, for example)? – johncg Jul 06 '13 at 14:03
  • Pointers won't help. If the variables are one type, and you need another type, a conversion will be necessary. If this makes your program too slow, you could either have the Fortran use `real (c_double)` instead of `real (8)` [which, in any case, is non-portable] everywhere or you could rewrite c_test in Fortran. Have you measured the execution time? Is this area slow and a significant fraction of the run time? – M. S. B. Jul 06 '13 at 15:32
  • No, I didn't measured the time. Actually, I have a legacy code in Fortran and I am developing only the C interface. What is known is that `n` can be potentially large, and this C routine can be called `n` times. So, such conversions certanly will make the code slower. No problem, at all. I guess that, in general, `c_double = 8`, and I can even omit the conversion. Thank you. – johncg Jul 06 '13 at 16:15
3

Why are you using transfer? If the C routine can work directly with the internal representation of x, then just pass it x. If you are converting from kind 8 to kind c_double, then just use assignment.

Does the C routine modify x or n (does it take n by value?)? Assuming not, and assuming tthat the use of transfer is not for some other unstated reason - on many platforms, the value of C_double is 8 and the default integer kind is the value of c_int. A shortcut would be worthwhile.

if (kind(x) == c_double .and. kind(n) == c_int) then
  call c_test(n, x, value)
else
  n_ = n
  x_ = x
  call c_test(n_, x_, value_)
  value = value_
end if
IanH
  • 21,026
  • 2
  • 37
  • 59
  • OK, this looks like a good workaround, thanks! But since `x` is a vector of size `n`, the assignment may consume `O(n)` time. Do you think is there another way to avoid this (something that consume constant time to execute)? – johncg Jul 06 '13 at 14:02