Memory Considerations

While all memory considerations are handled automatically when you write pure Prolog code, you need to take special care when interfacing to foreign languages. In this section we'll describe several of these aspects.

1. Memory Alignment

C compilers for 32-bit platforms will usually align data on dword boundaries, while those for 16-bit platforms will usually align on byte boundaries. The reason for aligning on word or dword boundaries is speed. On a 32-bit platform, dword alignment will give up to 10-12 percent faster execution than byte alignment.

For simple variables alignment isn't important, but in order for Prolog terms and C structures to be compatible, the data contained in the records must be identically aligned. To this end, Visual Prolog gives you the option of selecting a different alignment than what's the default for your platform. The default is dword if you're using a 32-bit version of Visual Prolog, otherwise byte.

The alignment scheme may be selected with the help of the Options | Project | Compiler Options menu item, or through the -A command line option. Additionally, the align compiler directive may be used to override alignment on selected domains, like this:

    DOMAINS
        dom = align { byte | word | dword } func(d1,d2,...) [; func1(...); ... ]

The align directive must appear before any alternatives in the domain, and all alternatives will have the alignment specified. It's not possible to specify different alignment for individual alternatives.

For functorless terms, the align directive should appear after the struct directive.

Note that when several processes share a database or communicate over pipes, it's crucial that the domains involved use identical alignment.

(1) Example

Byte alignment is easy: each element is simply put right after the previous one. Given the declaration dom = struct my_struct(char,short,char,long) (recall that the struct directive declares the term to be functorless), the term my_struct('P',29285,'B',1702063209) is stored in memory like this:

    Byte number:

0

1

2

3

4

5

6

7

'P'

29285

'B'

1702063209

Word and dword alignment is a bit trickier. Here, items are stored in memory so that accessing them won't cross a word or dword boundary. That means that the individual elements of terms may be followed by a number of unused bytes, depending on the size of the following element. With dword alignment, the term above would be stored like this:

Byte number:

0

1

2

3

4

5

6

7

8

9

10

11

'P'

PAD

29285

'B'

PAD

PAD

PAD

1702063209

 

The PADs indicate unused bytes, allowing the values following them to be stored on suitable boundaries.

Notice that it's sufficient for the value 29285 to be aligned on a word boundary, because it's a short (16 bits); accessing it on a word boundary won't cross any undesirable boundaries.

2. Memory Allocation

When you create and return compound objects to Visual Prolog, memory for the objects must normally be allocated on the Global Stack. This memory will automatically be released if you fail back to a point previous to its allocation. GStack memory is allocated using:

    void *MEM_AllocGStack(unsigned size);

You would typically use C's sizeof function to determine how much memory to allocate. Given for instance the mydom domain discussed previously, the Prolog declarations for a C routine returning a term belonging to that domain in an argument would be:

/* Program mydom_p.pro */

global domains

    mydom = i(integer); c(char); s(string)

 

global predicates

    determ make_mydom(mydom) - (o) language C

 

goal

    make_mydom(MD), write(MD), nl.

And the C code for mydom and make_mydom could be:

/* Program mydom_c.c */

 

void *MEM_AllocGStack(unsigned);

char *MEM_SaveStringGStack(char *);

 

void make_mydom(register MYDOM **md)

{

    *md = MEM_AllocGStack(sizeof(MYDOM));

    (*md)->func = 3;

    (*md)->u.s = MEM_SaveStringGStack("wombat");

}

Notice that, as terms are handled through pointers in Prolog, the argument to make_mydom is a pointer to a term pointer. This example also makes use of another GStack-related function, MEM_SaveStringGStack, which allocates GStack space for the string (based on its length), then copies the string into the allocated space, returning a pointer to it. There's a few other handy functions in Visual Prolog's library:

    char *MEM_SaveStringHeap(char *String);
    /* Copies String to heap */

    unsigned STR_StrLen(char *String);
    /* Returns length (excluding terminating null byte) of String */

    void MEM_MovMem(void *Source,void *Dest,unsigned Len);
    /* Moves Len bytes from Source to Dest; these may overlap */

(1) Pre-allocation of Memory

Many C library functions require you to specify a pointer to a structure, which the C routine then fills in. In this case the compound flow pattern for global predicates should be used to specify what's happening:

    GLOBAL DOMAINS
    off_t, time_t = long
        dev_t = short
        stat = struct stat(dev_t,ushort,ushort,short,ushort,ushort,
                                dev_t,off_t,time_t,time_t,time_t)

    GLOBAL PREDICATES
        determ integer stat(string,stat) -
                            
                    (i,stat(o,o,o,o,o,o,o,o,o,o,o)) language C

When you call stat

    ..., 0 = stat("/unix",Stat), !, write(Stat).

Visual Prolog will allocate memory for the stat structure before the call.

(2) The sizeof function

Visual Prolog has a sizeof function that duplicates C's sizeof function, returning the size of the specified domain or variable.

For a compound domain with alternatives, sizeof will return the size of the largest alternative. Given a second argument of a functor from one of the domain alternatives, sizeof will return the size of that particular alternative.

Given a variable, sizeof will return the size of the corresponding domain (alternative), except that for string variables or constants, sizeof will return the number of bytes in the string including the terminating zero byte.

The program align.pro illustrates alignment selection and the use of the sizeof function:

/* Program align.pro */

predicates

    refint(refint)

 

clauses

    refint(_).

 

goal                         % Find the size of a functorless domain

    A = sizeof(dom),

    write("¡¬nSize=",A),

 

% when there are alternatives, the largest is returned

    B = sizeof(dom1),

    write("¡¬nSize=",B),

 

% Find size of a single alternative

    C = sizeof(dom1,g),

    write("¡¬nSize=",C),

 

% Find size of a term pointed to by a variable

    X = f(1,1,1),                                 % This is from dom1

    D = sizeof(X),

    write("¡¬nSize=",D),

 

% Find size of a string pointed to by a variable

    Y = "hello there",

    E = sizeof(Y),

    write("¡¬nSize=",E),

 

% Find size of a reference variable

    refint(Z),

    F = sizeof(Z),

    write("¡¬nSize=",F).

Load and run this program. Try changing the domains and their alignment, and watch the results.

(3) malloc and free

When writing functions in other languages, you often need to allocate dynamic memory. You've already seen MEM_AllocGStack, but this allocates memory on Prolog's Global Stack, which is released automatically. Permanent allocations should be done in the heap, and because Visual Prolog already has suitable memory allocation routines, it's generally preferable to use these. In fact, in DOS it's mandatory to use them, since a foreign memory allocation package would be allocating memory from the same physical memory as Visual Prolog. On other platforms, you can use C's malloc and free, but this would duplicate an amount of code and data, and the two packages would both be holding released memory in separate pools. Moreover, Visual Prolog's heap allocation system has a performance far superior to that supplied with most C compilers.

Therefore, on all platforms except UNIX, public routines for malloc and free, consisting of bindings to Visual Prolog's heap allocation routines, are provided in the initialization assembler and object files. These files are found in the subdirectories for the different platforms and compilers in the FOREIGN directory of your distribution. Note that when linking, it's essential that the appropriate initialization file appears before the (C) library containing malloc and free.