From: Fernando L. <fer...@gm...> - 2010-04-05 18:07:17
|
Dear all, I'm new to the list, and want to soy Hello! posting some questions. ------ I'm working on a project related to a block diagram simulator and we have to move the actual implementation (C++ library + SWIG interface) to PyCXX. During the first approach to CXX, I've developed a simple class Block that accepts a parent (another Block) a name and a number ... The two files used until now are based on simple.cxx: The base.hxx file is """ #ifndef __BASE__ #define __BASE__ #include "CXX/Objects.hxx" #include "CXX/Extensions.hxx" #include "iostream" #include "string" #define ENDL "\n"; //class Block: public Py::PythonExtension< Block > class Block: public Py::PythonClass< Block > { Py::Object parent; Py::String name; Py::Int number; public: //Block( Py::String name_, Py::Int number_, Py::Object parent_ ) Block(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) : Py::PythonClass< Block >::PythonClass( self, args, kwds ) { args.verify_length(3); name = args[0]; number = args[1]; parent = args[2]; //std::cout << "sizeof() = " << sizeof(Block) << ENDL; } virtual ~Block() { //std::cout << "~Block "<<name<<" (auuughh)." << ENDL; } static void init_type(void) { behaviors().name( "Block" ); behaviors().doc( "Clase base para los bloques de simulacion" ); //behaviors().supportGetattr() ; //behaviors().supportSetattr(); behaviors().supportGetattro(); behaviors().supportSetattro(); PYCXX_ADD_NOARGS_METHOD( getFullName, getFullName, "obtener el nombre completo del bloque" ); //add_varargs_method("getFullName",&Block::getFullName,"getFullName()"); // Call to make the type ready for use behaviors().readyType(); } //static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { // return static_cast<PyObject*>(new Block(subtype,args,kwds)); //} //static void tp_dealloc(PyObject *obj) { // Block* k = static_cast<Block*>(obj); // Py::_XDECREF(*(k->parent)); // Py::_XDECREF(*(k->name)); // Py::_XDECREF(*(k->number)); // //delete k; // extension_object_deallocator(obj); //} //Py::Object getFullName( const Py::Tuple& args ) Py::Object getFullName( void ) { std::string r = ""; if( *parent != Py_None ) r += Py::String(PyObject_CallMethod(*parent,"getFullName","")).as_std_string( "utf-8" ) + "."; //if( parent != Py_None ) r = r.concat(p.name ).concat(Py::String(".")); r += name.as_std_string( "utf-8" ); return Py::String(r); } PYCXX_NOARGS_METHOD_DECL( Block, getFullName ) //Py::Object getattr( const char * name_ ) Py::Object getattro( const Py::String &name_ ) { //std::string aname( name_ ); std::string aname( name_.as_std_string( "utf-8" ) ); if( aname == "name" ) return name; if( aname == "number" ) return number; if( aname == "parent" ) return parent; //else return getattr_methods( name_ );; return genericGetAttro( name_ ); } int setattro( const Py::String &name_, const Py::Object &value ) { std::string aname( name_.as_std_string( "utf-8" ) ); if( aname == "name" ) { name = Py::String(value); return 0; } if( aname == "number" ) { number = Py::Int(value); return 0; } if( aname == "parent" ) { parent = value; return 0; } else { return genericSetAttro( name_, value ); } } }; #endif // __BASE__ """ The base.cxx file is: """ #include <assert.h> #include "base.h" class base_module : public Py::ExtensionModule<base_module> { public: base_module() : Py::ExtensionModule<base_module>( "base" ) // this must be name of the file on disk e.g. simple.so or simple.pyd { Block::init_type(); //add_varargs_method("Block", &base_module::factory_Block, "Block()"); // after initialize the moduleDictionary will exist initialize( "documentation for the simple module" ); //Py::Dict d( moduleDictionary() ); //d["var"] = Py::String( "var value" ); Py::Object x( Block::type() ); d["Block"] = x; } virtual ~base_module() {} private: /* Py::Object new_Block(const Py::Tuple& args) { if(args.length() != 3) throw Py::RuntimeError("Incorrect # of args to Block(name,number,parent)."); //return Py::asObject(new Block(Py::String(args[0]),Py::Int(args[1]),Py::Object(args[2]))); return Py::asObject(new Block(args)); } */ }; #if defined( PY3 ) static base_module *base; extern "C" PyObject *PyInit_base() { #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) Py::InitialisePythonIndirectPy::Interface(); #endif base = new base_module; return base->module().ptr(); } // symbol required for the debug version extern "C" PyObject *PyInit_base_d() { return PyInit_base(); } #else static base_module *base; extern "C" void initbase() { #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) Py::InitialisePythonIndirectPy::Interface(); #endif base = new base_module; } // symbol required for the debug version extern "C" void initbase_d() { initbase(); } #endif """ I'm using trunk version of the CXX files. The software works, creates the blocks, chain them, etc, but creating and destroying 1000000 base.Block objects finish with a 44MB memory usage (when 4MB is expected) (the sizeof(Block) is 40 bytes times 1000000 is 40MB !!!) I've tryed some variations creating Block as a PythonExtension successfully, an the memory doesn't grow with my 1000000 creation/deletion test process (no memory leak). This leads me to thinks that something in the deallocator for new_style_classes isn't working well. What do you think? ---- Also, I've made the test with the simple.cxx module from Demo directory, and the same thing happens (as the object is ligthier you have to create/destroy 10000000, or so, times to see the memory growing). Someone have any clue? Best regards, Fernando Libonati |
From: Barry S. <ba...@ba...> - 2010-04-06 17:42:54
|
On 5 Apr 2010, at 19:07, Fernando Libonati wrote: > Dear all, > > I'm new to the list, and want to soy Hello! posting some questions. > > ------ > > I'm working on a project related to a block diagram simulator and we have to move the actual implementation (C++ library + SWIG interface) to PyCXX. > During the first approach to CXX, I've developed a simple class Block that accepts a parent (another Block) a name and a number ... > The two files used until now are based on simple.cxx: I'll comment inline > > The base.hxx file is > """ > #ifndef __BASE__ > #define __BASE__ > #include "CXX/Objects.hxx" > #include "CXX/Extensions.hxx" > #include "iostream" > #include "string" Its odd to use "header" and not <header> why do you do this? > > #define ENDL "\n"; Use std::endl as its in the standard. > > //class Block: public Py::PythonExtension< Block > > class Block: public Py::PythonClass< Block > > { > Py::Object parent; > Py::String name; > Py::Int number; > public: > //Block( Py::String name_, Py::Int number_, Py::Object parent_ ) > Block(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) > : Py::PythonClass< Block >::PythonClass( self, args, kwds ) > { > args.verify_length(3); > name = args[0]; > number = args[1]; > parent = args[2]; > //std::cout << "sizeof() = " << sizeof(Block) << ENDL; > } > > virtual ~Block() > { > //std::cout << "~Block "<<name<<" (auuughh)." << ENDL; > } > > static void init_type(void) > { > behaviors().name( "Block" ); > behaviors().doc( "Clase base para los bloques de simulacion" ); > //behaviors().supportGetattr() > ; > //behaviors().supportSetattr(); > behaviors().supportGetattro(); > behaviors().supportSetattro(); > > > PYCXX_ADD_NOARGS_METHOD( getFullName, getFullName, "obtener el nombre completo del bloque" ); > //add_varargs_method("getFullName",&Block::getFullName,"getFullName()"); > > // Call to make the type ready for use > behaviors().readyType(); > } > > //static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { > // return static_cast<PyObject*>(new Block(subtype,args,kwds)); > //} > //static void tp_dealloc(PyObject *obj) { > // Block* k = static_cast<Block*>(obj); > // Py::_XDECREF(*(k->parent)); > // Py::_XDECREF(*(k->name)); > // Py::_XDECREF(*(k->number)); > // //delete k; > // extension_object_deallocator(obj); > //} PyCXX writes this tp_new and the other necessary functions for you. > > //Py::Object getFullName( const Py::Tuple& args ) > Py::Object getFullName( void ) > { > std::string r = ""; > if( *parent != Py_None ) You can use parent->isNone(). > r += Py::String(PyObject_CallMethod(*parent,"getFullName","")).as_std_string( "utf-8" ) + "."; You are freely mixing Pyhon C API and PyCXX. This means you have to worry about reference counting. Use the newly added callMemberFunction. Py::String( parent->callMemberFunction( "getFullName" ) ) > > //if( parent != Py_None ) r = r.concat(p.name).concat(Py::String(".")); > r += name.as_std_string( "utf-8" ); > return Py::String(r); > } > PYCXX_NOARGS_METHOD_DECL( Block, getFullName ) > > //Py::Object getattr( const char * name_ ) > Py::Object getattro( const Py::String &name_ ) > { > //std::string aname( name_ ); > std::string aname( name_.as_std_string( "utf-8" ) ); > > if( aname == "name" ) return name; > if( aname == "number" ) return number; > if( aname == "parent" ) return parent; > //else return getattr_methods( name_ );; > return genericGetAttro( name_ ); > } > > int setattro( const Py::String &name_, const Py::Object &value ) > { > std::string aname( name_.as_std_string( "utf-8" ) ); > > if( aname == "name" ) { > name = Py::String(value); > return 0; > } if( aname == "number" ) { > number = Py::Int(value); > return 0; > } if( aname == "parent" ) { > parent = value; > return 0; > } else { > return genericSetAttro( name_, value ); > } > } > > }; > > #endif // __BASE__ > """ > > The base.cxx file is: > """ > #include <assert.h> > #include "base.h" > > class base_module : public Py::ExtensionModule<base_module> > { > public: > base_module() > : Py::ExtensionModule<base_module>( "base" ) // this must be name of the file on disk e.g. simple.so or simple.pyd > { > Block::init_type(); > > //add_varargs_method("Block", &base_module::factory_Block, "Block()"); > // after initialize the moduleDictionary will exist > > initialize( "documentation for the simple module" ); > //Py::Dict d( moduleDictionary() ); > //d["var"] = Py::String( "var value" ); > Py::Object x( Block::type() ); > d["Block"] = x; > } > > virtual ~base_module() > {} > private: > /* Py::Object new_Block(const Py::Tuple& args) > { > if(args.length() != 3) throw Py::RuntimeError("Incorrect # of args to Block(name,number,parent)."); > //return Py::asObject(new Block(Py::String(args[0]),Py::Int(args[1]),Py::Object(args[2]))); > return Py::asObject(new Block(args)); > } > */ > }; > > #if defined( PY3 ) > static base_module *base; > > extern "C" PyObject *PyInit_base() > { > #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) > Py::InitialisePythonIndirectPy::Interface(); > #endif > > base = new base_module; > return base->module().ptr(); > } > > // symbol required for the debug version > extern "C" PyObject *PyInit_base_d() > { > return PyInit_base(); > } > > #else > > static base_module *base; > > extern "C" void initbase() > { > #if defined(PY_WIN32_DELAYLOAD_PYTHON_DLL) > Py::InitialisePythonIndirectPy::Interface(); > #endif > > base = new base_module; > } > > // symbol required for the debug version > extern "C" void initbase_d() > { > initbase(); > } > #endif > """ > > I'm using trunk version of the CXX files. > The software works, creates the blocks, chain them, etc, but creating and destroying 1000000 base.Block objects finish with a 44MB memory usage (when 4MB is expected) (the sizeof(Block) is 40 bytes times 1000000 is 40MB !!!) > > I've tryed some variations creating Block as a PythonExtension successfully, an the memory doesn't grow with my 1000000 creation/deletion test process (no memory leak). > > This leads me to thinks that something in the deallocator for new_style_classes isn't working well. > What do you think? > > ---- > > Also, I've made the test with the simple.cxx module from Demo directory, and the same thing happens (as the object is ligthier you have to create/destroy 10000000, or so, times to see the memory growing). > Someone have any clue? Can you post the python code you used to test simple.cxx which to show the memory leak please? On what python version and operating system are you doing this work? Barry |
From: Fernando L. <fer...@gm...> - 2010-04-07 15:17:14
Attachments:
simple.cxx
mytestsimple.py
|
Please see my answer below yours. > > I'll comment inline >> >> The base.hxx file is >> """ >> #ifndef __BASE__ >> #define __BASE__ >> #include "CXX/Objects.hxx" >> #include "CXX/Extensions.hxx" >> #include "iostream" >> #include "string" > > Its odd to use "header" and not <header> why do you do this? > Yes, it is a typo ... >> >> #define ENDL "\n"; > > Use std::endl as its in the standard. For some odd reason, the extension produced with mingw32 as compiler crashes when std::endl is used. I found that "\n" doesn't, but I don't know why. If I find some time to review this I will post a comment to mingw32. > >> >> class Block: public Py::PythonClass< Block > >> { >> Py::Object parent; >> Py::String name; >> Py::Int number; >> public: >> Block(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) >> : Py::PythonClass< Block >::PythonClass( self, args, kwds ) >> { >> args.verify_length(3); >> name = args[0]; >> number = args[1]; >> parent = args[2]; >> } >> >> virtual ~Block() >> { >> } >> >> static void init_type(void) >> { >> behaviors().name( "Block" ); >> behaviors().doc( "Clase base para los bloques de simulacion" ); >> //behaviors().supportSetattr(); >> behaviors().supportGetattro(); >> behaviors().supportSetattro(); >> >> >> PYCXX_ADD_NOARGS_METHOD( getFullName, getFullName, "obtener el >> nombre completo del bloque" ); >> >> >> // Call to make the type ready for use >> behaviors().readyType(); >> } >> >> //static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, >> PyObject *kwds) { >> // return static_cast<PyObject*>(new Block(subtype,args,kwds)); >> //} >> //static void tp_dealloc(PyObject *obj) { >> // Block* k = static_cast<Block*>(obj); >> // Py::_XDECREF(*(k->parent)); >> // Py::_XDECREF(*(k->name)); >> // Py::_XDECREF(*(k->number)); >> // //delete k; >> // extension_object_deallocator(obj); >> //} > > PyCXX writes this tp_new and the other necessary functions for you. Yes, I've commented those lines (it was a test based on some google search, but it doesn't work). > >> >> //Py::Object getFullName( const Py::Tuple& args ) >> Py::Object getFullName( void ) >> { >> std::string r = ""; >> if( *parent != Py_None ) > > You can use parent->isNone(). Ok > >> r += >> Py::String(PyObject_CallMethod(*parent,"getFullName","")).as_std_string( >> "utf-8" ) + "."; > > You are freely mixing Pyhon C API and PyCXX. This means you have to worry > about reference counting. > > Use the newly added callMemberFunction. > > Py::String( parent->callMemberFunction( "getFullName" ) ) Ok > >> #endif // __BASE__ >> """ >> >> I'm using trunk version of the CXX files. >> The software works, creates the blocks, chain them, etc, but creating and >> destroying 1000000 base.Block objects finish with a 44MB memory usage >> (when 4MB is expected) (the sizeof(Block) is 40 bytes times 1000000 is >> 40MB !!!) > > >> >> I've tryed some variations creating Block as a PythonExtension >> successfully, an the memory doesn't grow with my 1000000 creation/deletion >> test process (no memory leak). >> >> This leads me to thinks that something in the deallocator for >> new_style_classes isn't working well. >> What do you think? > > > >> >> ---- >> >> Also, I've made the test with the simple.cxx module from Demo directory, >> and the same thing happens (as the object is ligthier you have to >> create/destroy 10000000, or so, times to see the memory growing). >> Someone have any clue? > > Can you post the python code you used to test simple.cxx which to show the > memory leak please? Ok, see below > > On what python version and operating system are you doing this work? Python 2.5.2, Windows XP professional, Mingw32 (gcc 3.4.5 mingw-vista special r3) > > Barry > > Attached is the code that shows a memory increase in simple.cxx (with minor changes to avoid printing a lot when creating and destroying objects, and with the addition of a Py::String to the old_style_class, to make it the same content than new_style_class). When I run the mytestsimple.py, the python process memory usage starts at 4072kB, changes to 4080kB after the OLD style class test and then grows to 28.208kB after the NEW style test (these values are taken from the Windows Task Manager). I also run it with the "MemoryValidator" software, and it shows a HotSpot before the end (after new style test) Size Count HotSpot 271.872 107 1.06%, 271.872 bytes in 107 allocations. 271.872 107 PyWrapper_New : [{FUNC}PyWrapper_New Line 0] 25.165.824 96 98.16%, 25.165.824 bytes in 96 allocations. 25.165.824 96 PyObject_Dir : [{FUNC}PyObject_Dir Line 0] >From that I understand that the memory increase is located or related to a PyObject_Dir function. I only found one at cxxsupport.cxx, related to the dir() method of Py::Object, but I can't find where it is used in the application. -- Fernando Libonati fernando.libonati at gmail.com |
From: Barry S. <ba...@ba...> - 2010-04-09 21:07:18
|
I have reproduced the mem leak on the mac and fedora. I'm looking into the problem. Barry On 7 Apr 2010, at 16:17, Fernando Libonati wrote: > Please see my answer below yours. > >> >> I'll comment inline >>> >>> The base.hxx file is >>> """ >>> #ifndef __BASE__ >>> #define __BASE__ >>> #include "CXX/Objects.hxx" >>> #include "CXX/Extensions.hxx" >>> #include "iostream" >>> #include "string" >> >> Its odd to use "header" and not <header> why do you do this? >> > > Yes, it is a typo ... > >>> >>> #define ENDL "\n"; >> >> Use std::endl as its in the standard. > > For some odd reason, the extension produced with mingw32 as compiler > crashes when std::endl is used. I found that "\n" doesn't, but I don't > know why. If I find some time to review this I will post a comment to > mingw32. > >> >>> >>> class Block: public Py::PythonClass< Block > >>> { >>> Py::Object parent; >>> Py::String name; >>> Py::Int number; >>> public: >>> Block(Py::PythonClassInstance *self, Py::Tuple &args, Py::Dict &kwds) >>> : Py::PythonClass< Block >::PythonClass( self, args, kwds ) >>> { >>> args.verify_length(3); >>> name = args[0]; >>> number = args[1]; >>> parent = args[2]; >>> } >>> >>> virtual ~Block() >>> { >>> } >>> >>> static void init_type(void) >>> { >>> behaviors().name( "Block" ); >>> behaviors().doc( "Clase base para los bloques de simulacion" ); >>> //behaviors().supportSetattr(); >>> behaviors().supportGetattro(); >>> behaviors().supportSetattro(); >>> >>> >>> PYCXX_ADD_NOARGS_METHOD( getFullName, getFullName, "obtener el >>> nombre completo del bloque" ); >>> >>> >>> // Call to make the type ready for use >>> behaviors().readyType(); >>> } >>> >>> //static PyObject* tp_new(PyTypeObject *subtype, PyObject *args, >>> PyObject *kwds) { >>> // return static_cast<PyObject*>(new Block(subtype,args,kwds)); >>> //} >>> //static void tp_dealloc(PyObject *obj) { >>> // Block* k = static_cast<Block*>(obj); >>> // Py::_XDECREF(*(k->parent)); >>> // Py::_XDECREF(*(k->name)); >>> // Py::_XDECREF(*(k->number)); >>> // //delete k; >>> // extension_object_deallocator(obj); >>> //} >> >> PyCXX writes this tp_new and the other necessary functions for you. > > Yes, I've commented those lines (it was a test based on some google > search, but it doesn't work). > >> >>> >>> //Py::Object getFullName( const Py::Tuple& args ) >>> Py::Object getFullName( void ) >>> { >>> std::string r = ""; >>> if( *parent != Py_None ) >> >> You can use parent->isNone(). > > Ok > >> >>> r += >>> Py::String(PyObject_CallMethod(*parent,"getFullName","")).as_std_string( >>> "utf-8" ) + "."; >> >> You are freely mixing Pyhon C API and PyCXX. This means you have to worry >> about reference counting. >> >> Use the newly added callMemberFunction. >> >> Py::String( parent->callMemberFunction( "getFullName" ) ) > > Ok > >> >>> #endif // __BASE__ >>> """ >>> >>> I'm using trunk version of the CXX files. >>> The software works, creates the blocks, chain them, etc, but creating and >>> destroying 1000000 base.Block objects finish with a 44MB memory usage >>> (when 4MB is expected) (the sizeof(Block) is 40 bytes times 1000000 is >>> 40MB !!!) >> >> >>> >>> I've tryed some variations creating Block as a PythonExtension >>> successfully, an the memory doesn't grow with my 1000000 creation/deletion >>> test process (no memory leak). >>> >>> This leads me to thinks that something in the deallocator for >>> new_style_classes isn't working well. >>> What do you think? >> >> >> >>> >>> ---- >>> >>> Also, I've made the test with the simple.cxx module from Demo directory, >>> and the same thing happens (as the object is ligthier you have to >>> create/destroy 10000000, or so, times to see the memory growing). >>> Someone have any clue? >> >> Can you post the python code you used to test simple.cxx which to show the >> memory leak please? > > Ok, see below > >> >> On what python version and operating system are you doing this work? > > Python 2.5.2, Windows XP professional, Mingw32 (gcc 3.4.5 mingw-vista > special r3) > >> >> Barry >> >> > > Attached is the code that shows a memory increase in simple.cxx (with > minor changes to avoid printing a lot when creating and destroying > objects, and with the addition of a Py::String to the old_style_class, > to make it the same content than new_style_class). > > When I run the mytestsimple.py, the python process memory usage starts > at 4072kB, changes to 4080kB after the OLD style class test and then > grows to 28.208kB after the NEW style test (these values are taken > from the Windows Task Manager). > > I also run it with the "MemoryValidator" software, and it shows a > HotSpot before the end (after new style test) > Size Count HotSpot > 271.872 107 1.06%, 271.872 bytes in 107 allocations. > 271.872 107 PyWrapper_New : [{FUNC}PyWrapper_New Line 0] > > 25.165.824 96 98.16%, 25.165.824 bytes in 96 allocations. > 25.165.824 96 PyObject_Dir : [{FUNC}PyObject_Dir Line 0] > >> From that I understand that the memory increase is located or related > to a PyObject_Dir function. I only found one at cxxsupport.cxx, > related to the dir() method of Py::Object, but I can't find where it > is used in the application. > -- > Fernando Libonati > fernando.libonati at gmail.com > <simple.cxx><mytestsimple.py>------------------------------------------------------------------------------ > Download Intel® Parallel Studio Eval > Try the new software tools for yourself. Speed compiling, find bugs > proactively, and fine-tune applications for parallel performance. > See why Intel Parallel Studio got high marks during beta. > http://p.sf.net/sfu/intel-sw-dev_______________________________________________ > CXX-Users mailing list > CXX...@li... > https://lists.sourceforge.net/lists/listinfo/cxx-users |
From: Barry S. <ba...@ba...> - 2010-04-10 12:37:59
|
On 9 Apr 2010, at 22:07, Barry Scott wrote: > I have reproduced the mem leak on the mac and fedora. > > I'm looking into the problem. > > Barry This was hard to debug using any build of python with its own memory allocator enabled. This prevents malloc debug tools from finding the real location of the leak. I build python with --without-pymalloc which tells python to use malloc and free. valgrind then found the memory leak and a coding error. This is fixed in r232. Please test in your setup and confirm the memory leak is fixed. Barry |
From: Fernando L. <fer...@gm...> - 2010-04-10 15:23:07
|
Excellent ! I've updated my trunk copy to r232 and now the test with simple.cxx works fine without any leak. (I changed the PYCXX_ADD_..._METHOD sentences to the new syntax). The changes in ExtensionType.hxx are very subtle, and I can imagine the difficulty to find them. Again, thank you for the fast response. Fernando 2010/4/10 Barry Scott <ba...@ba...> > > On 9 Apr 2010, at 22:07, Barry Scott wrote: > > I have reproduced the mem leak on the mac and fedora. > > I'm looking into the problem. > > Barry > > > This was hard to debug using any build of python with > its own memory allocator enabled. This prevents malloc > debug tools from finding the real location of the leak. > > I build python with --without-pymalloc which tells python > to use malloc and free. > > valgrind then found the memory leak and a coding error. > > This is fixed in r232. Please test in your setup and confirm > the memory leak is fixed. > > Barry > > > > ------------------------------------------------------------------------------ > Download Intel® Parallel Studio Eval > Try the new software tools for yourself. Speed compiling, find bugs > proactively, and fine-tune applications for parallel performance. > See why Intel Parallel Studio got high marks during beta. > http://p.sf.net/sfu/intel-sw-dev > _______________________________________________ > CXX-Users mailing list > CXX...@li... > https://lists.sourceforge.net/lists/listinfo/cxx-users > > -- Fernando Libonati fernando.libonati at gmail.com |