Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I have to write a XS module that does some simulations with a lot of data (an array of ~10.000.000 structures). The simplified code is:

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "stdlib.h" struct s { int field1; int field2; int field3; }; void do_simulation() { int i; struct s* m = (struct s*)malloc(10000000 * sizeof(struct s)); // do some computation with data // but for simplifying things I'll do a simple initialization for ( i=0; i<10000000; i++ ) { m[i].field1 = 0; m[i].field2 = 1; m[i].field3 = 2; } free(m); } MODULE = MyModule PACKAGE = MyModule void sim() CODE: do_simulation();

Everything builds without problems, but the memory isn't freed after the function call. If I call do_simulation() function from a simple CPP file in a MS VS project, all is O.K. and the memory is freed instantly after function call.

The platform: Win32 XP SP2, ActiveState Perl 5.6.1, MS VS 6.0

What do I wrong?

Replies are listed 'Best First'.
Re: Calling a C function using malloc() in a XS
by BrowserUk (Patriarch) on Aug 08, 2010 at 11:36 UTC

    malloc() gets redefined by macro to one of the official memory allocation functions: Newx(), Newxc(), or Newxz(), but free() doesn't get redefined to Safefree().

    The solution is to use the official apis, not the crt functions.

    Or, if your memory allocations are never passed back to your perl code, #undef malloc at the top of your IC code.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Calling a C function using malloc() in a XS
by ikegami (Patriarch) on Aug 08, 2010 at 18:02 UTC
    Does
    void do_simulation() { struct s* m; m = (struct s*)malloc(10000000 * sizeof(struct s)); free(m); m = (struct s*)malloc(10000000 * sizeof(struct s)); free(m); }
    use more memory than
    void do_simulation() { struct s* m = (struct s*)malloc(10000000 * sizeof(struct s)); free(m); }

    It could be that the memory is being pout back into Perl's free memory pool and you just don't realize it.

Re: Calling a C function using malloc() in a XS
by Anonymous Monk on Aug 08, 2010 at 11:38 UTC
    See perlclib, replacements for standard C library functions
Re: Calling a C function using malloc() in a XS
by Anonymous Monk on Aug 08, 2010 at 11:53 UTC

    The same problem with New() and Safefree():

    #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "stdlib.h" struct s { int field1; int field2; int field3; }; void do_simulation() { int i; struct s* m; New(1, (struct s*)m, 10000000, struct s); // do some computation with data // but for simplifying things I'll do a simple initialization for ( i=0; i<10000000; i++ ) { m[i].field1 = 0; m[i].field2 = 1; m[i].field3 = 2; } Safefree(m); } MODULE = MyModule PACKAGE = MyModule void sim() CODE: do_simulation();

    Tested in a mod_perl environment and the memory continues to add up for each request (the final destination of this module will be the mod_perl environment)

      The same problem with New() and Safefree()

      I can't reproduce the problem using either malloc/free or New/safefree:
      use warnings; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; struct s { int field1; int field2; int field3; }; void do_simulation() { int i; struct s * m = (struct s*)malloc(10000000 * sizeof(struct s)); for (i=0; i<10000000; i++) { m[i].field1 = 0; m[i].field2 = 1; m[i].field3 = 2; } printf("Sleeping for 5\n"); sleep(5); free(m); printf("Memory freed ... sleep for another 5\n"); sleep(5); printf("Done\n"); } void do_simulation2() { int i; struct s* m; New(1, (struct s*)m, 10000000, struct s); for ( i=0; i<10000000; i++ ) { m[i].field1 = 0; m[i].field2 = 1; m[i].field3 = 2; } printf("Sleeping for 5\n"); sleep(5); Safefree(m); printf("Memory freed ... sleep for another 5\n"); sleep(5); printf("Done\n"); } EOC do_simulation(); do_simulation2();
      For me (perl-5.12.0, Win32) the memory is freed as soon as free/Safefree is called.

      Perhaps you're up against a feature of mod_perl ?

      Cheers,
      Rob

        Thank you, Rob, for your participation and reproducing the code on your Win32/perl-5-12 environment. I described my problem bearing in mind perl 5.6 (I know, it's an old version ... maybe even an archaic one), but your perl version triggered my intention to try a more recent perl.

        Firstly I wanted to try on a fresh environment. After reinstalling Win/MSVS6/Perl everything worked the same way ... Then I tried Perl 5.8 with the SAME (only #include "ppport.h" was added by h2xs) code and it worked correctly.

        Maybe it's premature to state that it's a Perl 5.6 (more precisely ActiveState Perl 5.6.1 Build 638) bug, but Perl 5.8 doesn't have this memory leak problem.

        Now I need to upgrade Perl/mod_perl. A last question would be what do you recommend for a 'not-enterprise production environment': 5.8, 5.10, or 5.12? Is there a stable mod_perl module built from 5.12 sources for Apache 2.2 (all Win32)? Or is it better to use mod_perl with Apache 2.0?

      You have #include "stdlib.h" after perl includes, get rid of it

        I've tried without "stdlib.h". The same result for both cases (malloc()/free() and New()/Safefree())

        I aslo created a new MSVC6 project with the same code (perl paths included):

        #include "EXTERN.h" #include "perl.h" #include "XSUB.h" struct s { int field1; int field2; int field3; }; void do_simulation() { int i; struct s * m = (struct s*)malloc(10000000 * sizeof(struct s)); for (i=0; i<10000000; i++) { m[i].field1 = 0; m[i].field2 = 1; m[i].field3 = 2; } /* sleep for 5 seconds to be able to analyze memory usage sleep(5); free(m); /* sleep for 5 seconds to be able to see if memory is freed sleep(5); } int main(void) { do_simulation(); return 1; }

        ... and everithing works as it should: first 5 seconds the process uses 200+ MB (VM included), the last 5 seconds memory usage drops down to ~600 KB

Re: Calling a C function using malloc() in a XS
by BrowserUk (Patriarch) on Aug 09, 2010 at 14:52 UTC

    If you really need to do this, then go to the OS direct for the memory and you can guarantee to be able to give it back when you're done with it:

    #! perl -slw use 5.010; use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => 'FreeMem', CLEAN_AFTER_BUILD => 0; struct s { int field1; int field2; int field3; }; #define SIZE ((int)10e6 * sizeof( struct s )) int test( SV *dummy ) { char buf[256]; struct s *m; printf( "CheckMem then enter"); gets( buf ); m= VirtualAlloc( NULL, SIZE, MEM_COMMIT, PAGE_READWRITE ); if( !m ) croak( "Couldn't allocate virtual memory: %d\n", GetLastE +rror() ); printf( "CheckMem then enter"); gets( buf ); VirtualFree( m, SIZE, MEM_DECOMMIT ); printf( "CheckMem then enter"); gets( buf ); return 1; } END_C test( 1 ); __END__ C:\test>freeMem-IC.pl CheckMem then enter 7.5MB CheckMem then enter 122.1MB CheckMem then enter 7.7MB

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.