evaleev / libint

Libint: high-performance library for computing Gaussian integrals in quantum mechanics
Other
225 stars 95 forks source link

How to properly destroy a libint2::Engine object #176

Closed javierelpianista closed 4 years ago

javierelpianista commented 4 years ago

Hello!

I've been using libint2 for a while and it's working wonderfully; it has significantly improved the capabilities of the software I'm maintaining. One thing I'd like to do is to properly destroy Engine objects so that they don't reside in memory. I've written this very basic program:

#include <iostream>                                                               
#include <string>                                                                 
#include <libint2.hpp>                                                            
#include <stdio.h>                                                                

void main() {                                                                     
    libint2::initialize(false);                                                   

    std::cout << "Hello!\n";                                                      

    std::string input_string;                                                     
    std::cout << "Defining engine\n";                                             
    std::getline(std::cin, input_string);                                         
    libint2::Engine engine = libint2::Engine(libint2::Operator::overlap, 1000, 6);
    std::cout << "Done\n";                                                        

    std::cout << "Finalizing\n";                                                  
    std::getline(std::cin, input_string);                                         
    libint2::finalize();                                                          
    std::cout << "Done\n";                                                        

    std::getline(std::cin, input_string);                                         
    std::cout << "Exiting now\n";                                                 
    std::getline(std::cin, input_string);                                         
}                                                                                 

The getline commands allow me to monitor memory usage by using Linux tools. After I create the Engine object, I see a bump in memory consumption from the program, and I was expecting it to fall down when I call finalize, but that's not the case; instead, memory isn't released until the program finishes. What is the proper way to destroy the Engine object once I've copied over all the results I needed?

evaleev commented 4 years ago

@javierelpianista thank you for using Libint!

the issue is that libint2::finalize() does not destroy Engine or deallocate its use. In fact, it does not do anything at the moment. Until your engine object is destroyed the memory will not be freed. You can test this by calling the destructor manually. Or, better, by enclosing the Defining engine and Finalizing blocks into one {} scope. When your program exits that scope engine will be destroyed and the memory will be freed.

Note that even when engine is destroyed you may not see any change in the memory consumption reported to the kernel. It is because you may be using a caching memory allocator which may (or may not) keep the memory block rather than release it back to the kernel. So tracking memory use in a C++ program can be more challenging than looking at the process stats.

javierelpianista commented 4 years ago

@evaleev thank you for your response. I've confirmed that the Engine objects are destroyed when they go out of scope (which they do in my actual code), but still the memory is occupied until my program finishes. Is there a way of estimating the amount of memory that is going to be occupied by these engines beforehand?

evaleev commented 4 years ago

There is no clean way to do this right now. You can look at the Engine ctor to figure out how much memory it allocates, but it is not particularly easy to read.

evaleev commented 4 years ago

my guess is your allocator caches the released memory. There is not much you can do about this other than replacing it with another implementation of malloc. E.g. tcmalloc has a way to flush its caches programmatically.

javierelpianista commented 4 years ago

@evaleev OK, I'll see what I can do. Again, thanks a lot!