Permalink
Browse files

Correctly reclaim std::vector unused memory.

Add new utility function to do that.
std::vector::resize() is not guaranteed reclaim unused memory.
Usually it will just change size, leaving same capacity.
To do that correctly we copy elements to temporary vector,
and afterwards swap it with ours. (Thanks Meyers :-)

Because this operation is expensive, don't do it in for each mod.
It should be performed afterwards.

Also, stop manully driving vector allocation size, since it is what
any sane standard library would do for you.
  • Loading branch information...
1 parent 4f887cf commit f980ac67feee5f377889464dc234d0eabb5a5e2b @piotrrak piotrrak committed Nov 18, 2012
Showing with 48 additions and 39 deletions.
  1. +48 −37 src/ItemManager.cpp
  2. +0 −2 src/ItemManager.h
View
@@ -37,6 +37,33 @@ FLARE. If not, see http://www.gnu.org/licenses/
using namespace std;
+/**
+ * Resizes vector vec, so it can fit index id.
+ */
+template <typename Ty_>
+static inline void ensureFitsId(vector<Ty_>& vec, int id)
+{
+ // id's are always greater or equal 1;
+ if (id < 1) return;
+
+ typedef typename vector<Ty_>::size_type VecSz;
+
+ if (vec.size() <= VecSz(id+1))
+ vec.resize(id+1);
+}
+
+/**
+ * Trims vector allocated memory to its size.
+ *
+ * Emulates C++2011 vector::shrink_to_fit().
+ * It is sometimes also called "swap trick".
+ */
+template <typename Ty_>
+static inline void shrinkVecToFit(std::vector<Ty_>& vec)
+{
+ if (vec.capacity() != vec.size())
+ std::vector<Ty_>(vec).swap(vec);
+}
ItemManager::ItemManager()
: color_normal(font->getColor("item_normal"))
@@ -47,8 +74,11 @@ ItemManager::ItemManager()
, color_penalty(font->getColor("item_penalty"))
, color_requirements_not_met(font->getColor("requirements_not_met"))
, color_flavor(font->getColor("item_flavor"))
- , items(vector<Item>())
{
+ // NB: 20 is arbitrary picked number, but it looks like good start.
+ items.reserve(20);
+ item_sets.reserve(5);
+
loadAll();
loadSounds();
loadIcons();
@@ -66,7 +96,6 @@ void ItemManager::loadAll() {
if (fileExists(test_path)) {
this->load(test_path);
- if (!items.empty()) shrinkItems();
}
test_path = PATH_DATA + "mods/" + mods->mod_list[i] + "/items/types.txt";
@@ -79,9 +108,22 @@ void ItemManager::loadAll() {
if (fileExists(test_path)) {
this->loadSets(test_path);
- if (!item_sets.empty()) shrinkItemSets();
}
}
+
+ /*
+ * Shrinks the items vector to the absolute needed size.
+ *
+ * While loading the items, the item vector grows dynamically. To have
+ * no much time overhead for reallocating the vector, a new reallocation
+ * is twice as large as the needed item id, which means in the worst case
+ * the item vector was reallocated for loading the last element, so the
+ * vector is twice as large as needed. This memory is definitly not used,
+ * so we can free it.
+ */
+ shrinkVecToFit(items);
+ shrinkVecToFit(item_sets);
+
if (items.empty()) fprintf(stderr, "No items were found.\n");
if (item_sets.empty()) printf("No item sets were found.\n");
}
@@ -104,10 +146,7 @@ void ItemManager::load(const string& filename) {
if (infile.key == "id") {
id_line = true;
id = toInt(infile.val);
- if (id > 0 && id >= (int)items.size()) {
- // *2 to amortize the resizing to O(log(n)).
- items.resize((2*id) + 1);
- }
+ ensureFitsId(items, id+1);
} else id_line = false;
if (id < 1) {
@@ -116,6 +155,7 @@ void ItemManager::load(const string& filename) {
}
if (id_line) continue;
+
if (infile.key == "name")
items[id].name = msg->get(infile.val);
else if (infile.key == "flavor")
@@ -299,10 +339,7 @@ void ItemManager::loadSets(const string& filename) {
if (infile.key == "id") {
id_line = true;
id = toInt(infile.val);
- if (id > 0 && id >= (int)item_sets.size()) {
- // *2 to amortize the resizing to O(log(n)).
- item_sets.resize((2*id) + 1);
- }
+ ensureFitsId(item_sets, id+1);
} else id_line = false;
if (id < 1) {
@@ -382,32 +419,6 @@ void ItemManager::loadIcons() {
}
/**
- * Shrinks the items vector to the absolute needed size.
- *
- * While loading the items, the item vector grows dynamically. To have
- * no much time overhead for reallocating the vector, a new reallocation
- * is twice as large as the needed item id, which means in the worst case
- * the item vector was reallocated for loading the last element, so the
- * vector is twice as large as needed. This memory is definitly not used,
- * so we can free it.
- */
-void ItemManager::shrinkItems() {
- unsigned i = items.size() - 1;
- while (items[i].name == "")
- i--;
-
- items.resize(i + 1);
-}
-
-void ItemManager::shrinkItemSets() {
- unsigned i = item_sets.size() - 1;
- while (item_sets[i].name == "")
- i--;
-
- item_sets.resize(i + 1);
-}
-
-/**
* Renders icons at small size or large size
* Also display the stack size
*/
View
@@ -197,8 +197,6 @@ class ItemManager {
void loadAll();
void loadSounds();
void loadIcons();
- void shrinkItems();
- void shrinkItemSets();
SDL_Color color_normal;
SDL_Color color_low;

0 comments on commit f980ac6

Please sign in to comment.