Re: Performance implications of large FFI constants

  • From: Peter Colberg <peter@xxxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Wed, 26 Sep 2012 11:28:17 -0400

On Wed, Sep 26, 2012 at 12:05:58PM +0200, Mike Pall wrote:
>   local ffi = require("ffi")
>   ffi.cdef[[
>   struct whatever {
>     static const int FOO = 42;
>     static const int BAR = 99;
>   };
>   ]]
>   local W = ffi.new("struct whatever")
>   print(W.FOO, W.BAR)
> 
> Yes, these are compiled to actual constants, just like enum
> constants (and unlike Lua table fields). Except that enum constants
> are global in C, even if defined inside a struct. Note that it's
> slightly more efficient to use an instance of the dummy struct (W)
> as a namespace than using the ctype.

Could one define non-integral constants more efficiently as well,
without resorting to a Lua table on top of a C library namespace?

In the MPICH2 implementation of MPI Version 2, there are dozens
of constants that are cast to an opaque handle type:

    #define MPI_CHAR ((MPI_Datatype)0x4c000101)
    #define MPI_SHORT ((MPI_Datatype)0x4c000203)
    #define MPI_INT ((MPI_Datatype)0x4c000405)

    #define MPI_COMM_WORLD ((MPI_Comm)0x44000000)
    #define MPI_COMM_SELF ((MPI_Comm)0x44000001)

Currently I generate a Lua table for these constants:

    ffi.cdef[[
    typedef struct MPI_Comm *__ptr32 MPI_Comm;
    typedef struct MPI_Datatype *__ptr32 MPI_Datatype;
    ]]

    -- load MPI library into global namespace, too,
    -- to avoid a segmentation fault in MPI_Init
    local mpi = ffi.load("mpi", true)

    local defs = {
        MPI_CHAR = ffi.cast("MPI_Datatype", 0x4c000101),
        MPI_SHORT = ffi.cast("MPI_Datatype", 0x4c000203),
        MPI_INT = ffi.cast("MPI_Datatype", 0x4c000405),

        MPI_COMM_WORLD = ffi.cast("MPI_Comm", 0x44000000),
        MPI_COMM_SELF = ffi.cast("MPI_Comm", 0x44000001),
    }

    return setmetatable(defs, {__index = mpi})


The reason I would like to have the constants with correct type,
rather than casting from an enum constant before a function call,
is to allow consistent bindings for different implementations.

In the OpenMPI implementation, the ABI uses global variables:

    extern struct ompi_predefined_communicator_t ompi_mpi_comm_world;
    extern struct ompi_predefined_communicator_t ompi_mpi_comm_self;

    #define MPI_COMM_WORLD ((MPI_Comm) ((void *) &(ompi_mpi_comm_world)))
    #define MPI_COMM_SELF ((MPI_Comm) ((void *) &(ompi_mpi_comm_self)))

Which currently I translate to:

    ffi.cdef[[
    typedef struct MPI_Comm *MPI_Comm;
    extern struct MPI_Comm MPI_COMM_WORLD asm("ompi_mpi_comm_world");
    extern struct MPI_Comm MPI_COMM_SELF asm("ompi_mpi_comm_self");
    ]]

    local mpi = ffi.load("mpi", true)

    local defs = {
        MPI_COMM_WORLD = ffi.cast("MPI_Comm", mpi.MPI_COMM_WORLD),
        MPI_COMM_SELF = ffi.cast("MPI_Comm", mpi.MPI_COMM_SELF),
    }

    return setmetatable(defs, {__index = mpi})


Is there a better way of binding the above with LuaJIT?

Thanks,
Peter

Other related posts: