[arrayfire] 269/284: Changes to internal memory manager

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Sun Feb 7 18:59:41 UTC 2016


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/experimental
in repository arrayfire.

commit a1b7f8c55032d12350f9cef07a25d1b94a9e3042
Author: Pavan Yalamanchili <pavan at arrayfire.com>
Date:   Tue Feb 2 19:15:20 2016 -0500

    Changes to internal memory manager
    
    - Manager now contains list of locked and free buffers separately
    - Should improve performance when allocationg new buffers
    - Added proper documentation
---
 docs/pages/configuring_arrayfire_environment.md |  25 +++
 src/backend/MemoryManager.cpp                   | 227 +++++++++++++-----------
 src/backend/MemoryManager.hpp                   |  14 +-
 3 files changed, 160 insertions(+), 106 deletions(-)

diff --git a/docs/pages/configuring_arrayfire_environment.md b/docs/pages/configuring_arrayfire_environment.md
index a9ec486..d554046 100644
--- a/docs/pages/configuring_arrayfire_environment.md
+++ b/docs/pages/configuring_arrayfire_environment.md
@@ -142,3 +142,28 @@ When the environment variable is not set, it is treated to be non zero.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 AF_MEM_DEBUG=1 ./myprogram
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+AF_MAX_BUFFERS {#af_max_buffers}
+-------------------------------------------------------------------------
+
+When AF_MAX_BUFFERS is set, this environment variable specifies the maximum number of buffers allocated before garbage collection kicks in.
+
+Please note that the total number of buffers that can exist simultaneously can be higher than this number. This variable tells the garbage collector that it should free any available buffers immediately if the treshold is reached.
+
+When not set, the default value is 1000.
+
+AF_OPENCL_MAX_JIT_LEN {#af_opencl_max_jit_len}
+-------------------------------------------------------------------------------
+
+When set, this environment variable specifies the maximum length of the OpenCL JIT tree after which evaluation is forced. The default value for this is 16 for AMD devices and 20 otherwise.
+
+AF_CUDA_MAX_JIT_LEN {#af_cuda_max_jit_len}
+-------------------------------------------------------------------------------
+
+When set, this environment variable specifies the maximum length of the CUDA JIT tree after which evaluation is forced. The default value for this is 20.
+
+AF_CPU_MAX_JIT_LEN {#af_cpu_max_jit_len}
+-------------------------------------------------------------------------------
+
+When set, this environment variable specifies the maximum length of the CPU JIT tree after which evaluation is forced. The default value for this is 20.
diff --git a/src/backend/MemoryManager.cpp b/src/backend/MemoryManager.cpp
index 5773c19..b66dfc3 100644
--- a/src/backend/MemoryManager.cpp
+++ b/src/backend/MemoryManager.cpp
@@ -26,19 +26,32 @@ MemoryManager::MemoryManager(int num_devices, unsigned MAX_BUFFERS, bool debug):
     debug_mode(debug)
 {
     lock_guard_t lock(this->memory_mutex);
-    std::string env_var = getEnvVar("AF_MEM_DEBUG");
+
+    for (int n = 0; n < num_devices; n++) {
+        // Calling getMaxMemorySize() here calls the virtual function that returns 0
+        // Call it from outside the constructor.
+        memory[n].max_bytes     = ONE_GB;
+        memory[n].total_bytes   = 0;
+        memory[n].total_buffers = 0;
+        memory[n].lock_bytes    = 0;
+        memory[n].lock_buffers  = 0;
+    }
+
+    // Check for environment variables
+
+    std::string env_var;
+
+    // Debug mode
+    env_var = getEnvVar("AF_MEM_DEBUG");
     if (!env_var.empty()) {
         this->debug_mode = env_var[0] != '0';
     }
     if (this->debug_mode) mem_step_size = 1;
 
-    for (int n = 0; n < num_devices; n++) {
-        // Calling getMaxMemorySize() here calls the virtual function that returns 0
-        // Call it from outside the constructor.
-        memory[n].max_bytes    = ONE_GB;
-        memory[n].total_bytes  = 0;
-        memory[n].lock_bytes   = 0;
-        memory[n].lock_buffers = 0;
+    // Max Buffer count
+    env_var = getEnvVar("AF_MAX_BUFFERS");
+    if (!env_var.empty()) {
+        this->max_buffers = std::max(1, std::stoi(env_var));
     }
 }
 
@@ -61,28 +74,17 @@ void MemoryManager::garbageCollect()
     lock_guard_t lock(this->memory_mutex);
     memory_info& current = this->getCurrentMemoryInfo();
 
-    for(buffer_iter iter = current.map.begin();
-        iter != current.map.end(); ++iter) {
-
-        if (!(iter->second).manager_lock) {
-
-            if (!(iter->second).user_lock) {
-                if ((iter->second).bytes > 0) {
-                    this->nativeFree(iter->first);
-                }
-                current.total_bytes -= iter->second.bytes;
-            }
-        }
-    }
-
-    buffer_iter memory_curr = current.map.begin();
-    buffer_iter memory_end  = current.map.end();
-
-    while(memory_curr != memory_end) {
-        if (memory_curr->second.manager_lock || memory_curr->second.user_lock) {
-            ++memory_curr;
-        } else {
-            current.map.erase(memory_curr++);
+    // Return if all buffers are locked
+    if (current.total_buffers == current.lock_buffers) return;
+
+    for (auto &kv : current.free_map) {
+        size_t num_ptrs = kv.second.size();
+        //Free memory by popping the last element
+        for (int n = num_ptrs-1; n >= 0; n--) {
+            this->nativeFree(kv.second[n]);
+            current.total_bytes -= kv.first;
+            current.total_buffers--;
+            kv.second.pop_back();
         }
     }
 }
@@ -92,25 +94,47 @@ void MemoryManager::unlock(void *ptr, bool user_unlock)
     lock_guard_t lock(this->memory_mutex);
     memory_info& current = this->getCurrentMemoryInfo();
 
-    buffer_iter iter = current.map.find((void *)ptr);
+    locked_iter iter = current.locked_map.find((void *)ptr);
+
+    // Pointer not found in locked map
+    if (iter == current.locked_map.end()) {
+        // Probably came from user, just free it
+        this->nativeFree(ptr);
+        return;
+    }
 
-    if (iter != current.map.end()) {
+    if (user_unlock) {
+        (iter->second).user_lock = false;
+    } else {
+        (iter->second).manager_lock = false;
+    }
 
-        iter->second.manager_lock = false;
-        if ((iter->second).user_lock && !user_unlock) return;
+    // Return early if either one is locked
+    if ((iter->second).user_lock || (iter->second).manager_lock) return;
 
-        iter->second.user_lock = false;
-        current.lock_bytes -= iter->second.bytes;
-        current.lock_buffers--;
+    size_t bytes = iter->second.bytes;
+    current.lock_bytes -= iter->second.bytes;
+    current.lock_buffers--;
 
-        if (this->debug_mode) {
-            if ((iter->second).bytes > 0) {
-                this->nativeFree(iter->first);
-            }
-        }
+    current.locked_map.erase(iter);
 
+    if (this->debug_mode) {
+        // Just free memory in debug mode
+        if ((iter->second).bytes > 0) {
+            this->nativeFree(iter->first);
+        }
     } else {
-        this->nativeFree(ptr); // Free it because we are not sure what the size is
+        // In regular mode, move buffer to free map
+        free_iter fiter = current.free_map.find(bytes);
+        if (fiter != current.free_map.end()) {
+            // If found, push back
+            fiter->second.push_back(ptr);
+        } else {
+            // If not found, create new vector for this size
+            std::vector<void *> ptrs;
+            ptrs.push_back(ptr);
+            current.free_map[bytes] = ptrs;
+        }
     }
 }
 
@@ -129,45 +153,41 @@ void *MemoryManager::alloc(const size_t bytes, bool user_lock)
 
             // FIXME: Add better checks for garbage collection
             // Perhaps look at total memory available as a metric
-            if (current.map.size() > this->max_buffers ||
-                current.lock_bytes >= current.max_bytes) {
-
+            if (current.lock_bytes >= current.max_bytes ||
+                current.total_buffers >= this->max_buffers) {
                 this->garbageCollect();
             }
 
-            for(buffer_iter iter = current.map.begin();
-                iter != current.map.end(); ++iter) {
-
-                buffer_info info = iter->second;
-
-                if (!info.manager_lock &&
-                    !info.user_lock &&
-                    info.bytes == alloc_bytes) {
+            free_iter iter = current.free_map.find(alloc_bytes);
 
-                    iter->second.manager_lock = true;
-                    current.lock_bytes += alloc_bytes;
-                    current.lock_buffers++;
-                    return iter->first;
-                }
+            if (iter != current.free_map.end() && !iter->second.empty()) {
+                ptr = iter->second.back();
+                iter->second.pop_back();
             }
+
         }
 
-        // Perform garbage collection if memory can not be allocated
-        try {
-            ptr = this->nativeAlloc(alloc_bytes);
-        } catch (AfError &ex) {
-            // If out of memory, run garbage collect and try again
-            if (ex.getError() != AF_ERR_NO_MEM) throw;
-            this->garbageCollect();
-            ptr = this->nativeAlloc(alloc_bytes);
+        // Only comes here if buffer size not found or in debug mode
+        if (ptr == NULL) {
+            // Perform garbage collection if memory can not be allocated
+            try {
+                ptr = this->nativeAlloc(alloc_bytes);
+            } catch (AfError &ex) {
+                // If out of memory, run garbage collect and try again
+                if (ex.getError() != AF_ERR_NO_MEM) throw;
+                this->garbageCollect();
+                ptr = this->nativeAlloc(alloc_bytes);
+            }
+            // Increment these two only when it succeeds to come here.
+            current.total_bytes += alloc_bytes;
+            current.total_buffers += 1;
         }
 
-        buffer_info info = {true, false, alloc_bytes};
-        current.map[ptr] = info;
 
+        locked_info info = {true, user_lock, alloc_bytes};
+        current.locked_map[ptr] = info;
         current.lock_bytes += alloc_bytes;
         current.lock_buffers++;
-        current.total_bytes += alloc_bytes;
     }
     return ptr;
 }
@@ -178,34 +198,22 @@ void MemoryManager::userLock(const void *ptr)
 
     lock_guard_t lock(this->memory_mutex);
 
-    buffer_iter iter = current.map.find(const_cast<void *>(ptr));
+    locked_iter iter = current.locked_map.find(const_cast<void *>(ptr));
 
-    if (iter != current.map.end()) {
+    if (iter != current.locked_map.end()) {
         iter->second.user_lock = true;
     } else {
-        buffer_info info = { true,
-                          true,
-                          100 }; //This number is not relevant
+        locked_info info = {false,
+                            true,
+                            100}; //This number is not relevant
 
-        current.map[(void *)ptr] = info;
+        current.locked_map[(void *)ptr] = info;
     }
 }
 
 void MemoryManager::userUnlock(const void *ptr)
 {
-    memory_info& current = this->getCurrentMemoryInfo();
-
-    lock_guard_t lock(this->memory_mutex);
-
-    buffer_iter iter = current.map.find((void *)ptr);
-    if (iter != current.map.end()) {
-        iter->second.user_lock = false;
-        if (this->debug_mode) {
-            if ((iter->second).bytes > 0) {
-                this->nativeFree(iter->first);
-            }
-        }
-    }
+    this->unlock(const_cast<void *>(ptr), true);
 }
 
 size_t MemoryManager::getMemStepSize()
@@ -237,32 +245,47 @@ void MemoryManager::printInfo(const char *msg, const int device)
     static const std::string line(head.size(), '-');
     std::cout << line << std::endl << head << std::endl << line << std::endl;
 
-    for(buffer_iter iter = current.map.begin();
-        iter != current.map.end(); ++iter) {
-
-        std::string status_mngr("Unknown");
+    for(auto& kv : current.locked_map) {
+        std::string status_mngr("Yes");
         std::string status_user("Unknown");
-
-        if(iter->second.manager_lock)  status_mngr = "Yes";
-        else                           status_mngr = " No";
-
-        if(iter->second.user_lock)     status_user = "Yes";
-        else                           status_user = " No";
+        if(kv.second.user_lock)     status_user = "Yes";
+        else                        status_user = " No";
 
         std::string unit = "KB";
-        double size = (double)(iter->second.bytes) / 1024;
+        double size = (double)(kv.second.bytes) / 1024;
         if(size >= 1024) {
             size = size / 1024;
             unit = "MB";
         }
 
-        std::cout << "|  " << std::right << std::setw(14) << iter->first << " "
+        std::cout << " |  " << std::right << std::setw(14) << kv.first << " "
                   << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit
                   << " | " << std::setw(9) << status_mngr
                   << " | " << std::setw(9) << status_user
                   << " |"  << std::endl;
     }
 
+    for(auto &kv : current.free_map) {
+
+        std::string status_mngr("No");
+        std::string status_user("No");
+
+        std::string unit = "KB";
+        double size = (double)(kv.first) / 1024;
+        if(size >= 1024) {
+            size = size / 1024;
+            unit = "MB";
+        }
+
+        for (auto &ptr : kv.second) {
+            std::cout << " |  " << std::right << std::setw(14) << ptr << " "
+                      << " | " << std::setw(7) << std::setprecision(4) << size << " " << unit
+                      << " | " << std::setw(9) << status_mngr
+                      << " | " << std::setw(9) << status_user
+                      << " |"  << std::endl;
+        }
+    }
+
     std::cout << line << std::endl;
 }
 
@@ -272,7 +295,7 @@ void MemoryManager::bufferInfo(size_t *alloc_bytes, size_t *alloc_buffers,
     lock_guard_t lock(this->memory_mutex);
     memory_info current = this->getCurrentMemoryInfo();
     if (alloc_bytes   ) *alloc_bytes   = current.total_bytes;
-    if (alloc_buffers ) *alloc_buffers = current.map.size();
+    if (alloc_buffers ) *alloc_buffers = current.total_buffers;
     if (lock_bytes    ) *lock_bytes    = current.lock_bytes;
     if (lock_buffers  ) *lock_buffers  = current.lock_buffers;
 }
diff --git a/src/backend/MemoryManager.hpp b/src/backend/MemoryManager.hpp
index a010f30..015fa6d 100644
--- a/src/backend/MemoryManager.hpp
+++ b/src/backend/MemoryManager.hpp
@@ -29,17 +29,23 @@ class MemoryManager
         bool manager_lock;
         bool user_lock;
         size_t bytes;
-    } buffer_info;
+    } locked_info;
 
-    typedef std::map<void *, buffer_info> buffer_t;
-    typedef buffer_t::iterator buffer_iter;
+    typedef std::map<void *, locked_info> locked_t;
+    typedef locked_t::iterator locked_iter;
+
+    typedef std::map<size_t, std::vector<void *> >free_t;
+    typedef free_t::iterator free_iter;
 
     typedef struct
     {
-        buffer_t map;
+        locked_t locked_map;
+        free_t   free_map;
+
         size_t lock_bytes;
         size_t lock_buffers;
         size_t total_bytes;
+        size_t total_buffers;
         size_t max_bytes;
     } memory_info;
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/arrayfire.git



More information about the debian-science-commits mailing list