Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Force Release vof Memory Not Dynamically Allocated to Heap (C++08)

This thread is locked; no one can reply to it. rss feed Print
Force Release vof Memory Not Dynamically Allocated to Heap (C++08)
ZoriaRPG
Member #16,714
July 2017
avatar

Is this possible, at all?

Let's say that I have an object that persists for a span of time, and that there are multiple instances of that object, derived from a constructor.

The constructor class has something like int foo[512];

I was wondering if there's way to deallocate the RAM used by foo[] without needing to malloc it, or to use the new token, as I don't want to need to do manual cleanup on all of these objects when they're destroyed.

I simply want to ensure that instances that never need that array, don't use RAM for it. Is that legal; or do I need a separate constructor for those, that never has that array?

Further, in C++08 polymorphism, do derived classes automatically inherit the array, even if they don't declare it themselves?

e.g.

class foo
{
    public:
    u_int32 myarr[512];
    u_int32 secondarr[512];
};

class bar : public foo
{
    public:
    u_int32 myarr[512];
};

Does 'bar' also automatically inherit secondarray[]? if not, then I can do something like that to prevent these objects having this array, that they'll never use, and thus save RAM. ???

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Even C++08 should have std::vector :

std::vector<int> vector_array(512 , (int)0);
vector_array[blah]...;
vector_array.clear();

And yes, a derived class object inherits all the objects that the parent holds, according to its access modifier :

#SelectExpand
1class B { 2private : 3 int priv; 4protected : 5 int prot; 6public : 7 int pub; 8}; 9 10class C : public B {/* holds priv,prot,and pub */}; 11class D : protected B { 12/// B::priv is not accessible 13/// B::prot is accessible 14/// B::pub is accessible 15}; 16class E : private B { 17/// B::priv is not accessible 18/// B::prot is not accessible 19/// B::pub is still accessible 20};

I don't see what you're trying to do though. Those arrays you declared as class members are allocated on whatever the medium was that their class was created on, be it the heap or the stack.

ZoriaRPG said:

I simply want to ensure that instances that never need that array, don't use RAM for it. Is that legal; or do I need a separate constructor for those, that never has that array?

Then don't allocate it? Use a pointer, and only allocate it if you need it. new and delete, new[] and delete[], malloc and free.

ZoriaRPG
Member #16,714
July 2017
avatar

std::vector isn't a viable option for this (and my sanity)... I'll just make a new base class for these sprited objects that should never have a number of properties, so that they never waste RAM. 'Particle' sprites are the big offenders, because there can be hundreds, or thousands of them at a time.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Unless you have a good reason not to use it, a vector is exactly what you asked for. Array semantics. Preallocation. Easy to free. Lives on the stack. Uses memory on the heap. You fully admit you're using C++ but you don't want to use the STL. Sigh.

Your only other option is manual dynamic memory allocation and deallocation, or not to derive from a class that holds data it won't use, which makes sense.

Base classes should typically only be an interface. The more state they carry, the heavier they get to use properly.

ZoriaRPG
Member #16,714
July 2017
avatar

It's complicated, but in short, when if/when I convert script stacks into vectors, I want to do all of them, and that's far more work at present than I want to perform, and to test, while adding this into the system.

There's quite a lot of code that relies on them being basic long arrays at present, and naturally it's spread about everywhere.

I have no clude who decided that all of these game objects should be derived from one class as a template, but I would never have done it that way. C++ wasn't my choice, you know. :P

It's far simpler to move the decorations and particles into their own templates, because far less uses them.

torhu
Member #2,727
September 2002
avatar

ZoriaRPG said:

There's quite a lot of code that relies on them being basic long arrays at present, and naturally it's spread about everywhere.

How does it rely on them being arrays? Syntax, memory layout, something else?

Chris Katko
Member #1,881
January 2002
avatar

"The title of my blog post is word play: please use vectors, but use them properly if you need speed!"

https://lemire.me/blog/2012/06/20/do-not-waste-time-with-stl-vectors/

There are also plenty of alternatives to STL vectors:

https://www.boost.org/doc/libs/1_58_0/doc/html/container/non_standard_containers.html

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Peter Hull
Member #1,136
March 2001

It didn't come in until C++11 but std::array gives you all the memory management of C++ but allows you to get a raw data pointer for compatibility.

Is this code from ZeldaClassic? Maybe you could post a link to a file you think is particularly 'bad' and people could give you some more concrete solutions.

Pete

torhu
Member #2,727
September 2002
avatar

You can get a raw data pointer from vector too. Either by taking the address of the first element, or by calling the data method that was added in C++11.

Peter Hull
Member #1,136
March 2001

True but if you store that pointer somewhere it might become invalid if the vector resizes. Though in this case if I understand correctly the vectors wouldn't be resized.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Note that you can cheat and get the same speed as a C++ new by constructing the vector empty, then reserving the memory (reserve) and never actually resizing the array.

Well, duh, you don't want to use push_back to initialize a vector, or to sum 1 to N. His test kind of shows the obvious.

ZoriaRPG
Member #16,714
July 2017
avatar

torhu said:

How does it rely on them being arrays? Syntax, memory layout, something else?

A little of both.

The arrays are used for script stacks, and I don't want to use a vector for the sprite stacks, but not elsewhere. The syntax and structure should be uniform.

Isn't vector access (C++03) slower, too? I certainly want to avood adding even more impediments into the system.

Is this code from ZeldaClassic? Maybe you could post a link to a file you think is particularly 'bad' and people could give you some more concrete solutions.

It's for ZC, specifically ZScript stacks for sprited objects.

I wish it was just one file. It's spread out everwhere, but I can give you a few commits if you're curious.

https://github.com/ArmageddonGames/ZeldaClassic/commit/484edc9dca24fc71f3c61855680dce3ca3376fe9

https://github.com/ArmageddonGames/ZeldaClassic/commit/b3304481efe45122e73c9eaa0b1f0a3780eb5d1e

https://github.com/ArmageddonGames/ZeldaClassic/commit/0bc2d4e76e878f40979c4090db2e5944d882a5cb

That one is notable, as it has pointer conversion of the stacks in it. The majority of my commits from the last week are on this subject, and sadly, I'm the only one working on this aspect of the programme at present.

I want to finish all new script types by the end of the month, to get a build out with them for people to play with during the holidays, so, time is also a factor.

Anyway, I think that until I'm ready to convert all of the stacks, and the global registers to vectors, that making new classes for decoration and particles is the best solution for RAM conservation.

It wouldn't be a bad idea for them to be separate anyway. That particles are also sprites also causes an issue with sprite counts, which I want to avoid. The solutions for that are:

(1) Increase maximum sprite count universally from 255 to 65280 (!!).
(2) Somehow make a lot of exceptions for particles when determining counts.
(3) Don't make particles derive from sprite class objects.

The third choice is cleaner, all-around.

P.S. Full backward-compatibility is the most important priority when we add new features, so bear in mind that we can't change how stuff used to work, unless any refactoring ensures that it still (non-mechanically) works in precisely the same manner.

All quests and scripts made in earlier versions still must work properly, without being edited or changed, in the new versions of ZC. It's part of the programme standard.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

ZoriaRPG said:

Isn't vector access (C++03) slower, too? I certainly want to avood adding even more impediments into the system.

No, that's not true. A vector is just a wrapper around a dynamically allocated array that resizes itself appropriately.

And you can't actually lower capacity of the vector without replacing it, but this is easy, as vector construction semantics from input iterators work nicely to copy only the front of an array. And they can be used to initialized from pointers as well.

Chris Katko
Member #1,881
January 2002
avatar

ZoriaRPG said:

(1) Increase maximum sprite count universally from 255 to 65280 (!!).

I don't... why haven't you done it already?

If you truly want "Speed", then you shouldn't be allocating anyway. You get one fixed chunk of memory (an array) and only use the indices you want. You also increase speed by sorting your indices so that the full and empty sets are at opposite ends with no overlap. The CPU branch predictor goes from "always true" to "always false" a total of ONE time. A single mispredict.

"But it uses more RAM!"

And?

"But I care about RAM."

1. Optimize for one thing. You cannot have your cake and eat it too.

2. Have you done actual math, to see if having PARTICLE_MAX entries will use up "too much"? Have you even defined "too much" for your target platform, or are you simply going on feelings of "this feels like a waste"?

Because if your program CAN swell to say, 32MB, and runs on people's machines... then why do you care if it ever takes LESS than that? Oh, sure, you get it down to 24MB by carefully allocating only the needed assets... and then you change to a big level and it goes back to 32MB. If it CAN swell to a large size, then the user NEEDS that much RAM regardless. They can't play 90% of the game and then just give up on the final boss because your careful allocation strategy saved RAM until the boss was loaded in and then it needed PARTICLE_MAX number of particles flying around.

You're making a game, not a windows service. You have the user's undivided attention. You're not running 300 of them in the background at a time on a server. They want the game to run, and run well. That's it. The NUMBERS like "ram usage" don't matter at all as long as the user experience is actually solid.

It seems like your focusing on "tuning" your project like it's a hotrod and every little ounce of tweaking improves it. It's not a hotrod. It's a burger joint and people only care if the burgers get delivered in a timely matter and at a reasonable cost. A good burger that costs 30 cents less than it did before, won't gain a single extra sale. People want good burgers, not 30 extra cents lying around in their pockets.

So because games take incredible amount of hours to make, optimize for production efficency. Getting the game out the door with features people care about. People aren't downloading a memory allocator, or particle system. They're downloading a game.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Peter Hull
Member #1,136
March 2001

Are you referring to class sprite and class particle?

If so, I agree it does look extremely wasteful of memory. Sprite is a heavyweight class (it has over 2000 bytes of data) and each individual particle will also inherit this. As CK says though, is this definitely a problem? Say if you had a hundred particles, that's 200K which is not a big deal these days.

I suppose more and more functionality has been pushed into sprite over the years so it's become a bit like what people call a 'god class'.

No easy solution I'm afraid. Maybe look at the shared behaviour/properties of sprites and particles and extract a common base class?

Reference:
class particle
class sprite

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

particles definitely should NOT inherit from sprite.

a particle is a dumb effect. It does not need to know about scripts or sprites, or anything else. At most it's a list of positions and velocities away from a central point with a shared image.

ZoriaRPG
Member #16,714
July 2017
avatar

I agree. I didn't create that class, of course. I have no clue who thought that particles should in herit from sprite, but it certainly should not.

ANd aye, when you can literally have 65280 particles on the same game frame, that persist for a few frames, that's plenty-enough wasted memory that's doing nothing.

Some of our users still run Windows XP with 2GB of RAM, and there's no excuse not to control this waste.

Chris Katko
Member #1,881
January 2002
avatar

But how big are they? In bytes, I mean.

And why that specific number? Why 65280? That's not even a power of two. Why not 2048? Or 4096?

ZoriaRPG said:

Don't make particles derive from sprite class objects.

DEAR GOSH, NO. NEVER. NEVER. NNEEEVVVEr.

Just others said. Particles should be a ROOT object. They're special because they're dumb, and there's TONS of them, so it's worth making a distinction from normal objects.

You should have a fixed array of MAX_PARTICLES. All "alive" or "used" particles are kept at the front. So if it's half filled, the front half is used. When a random particle is "deleted" you SWAP IT with the last living particle ($current_number_of_particles), and then decrement $current_number_of_particles. So now what was the last living particle is now a "deleted" one, and you've "resized" your array with a single decrement.

Also,

2 GB of RAM is still a huge amount.

[65536 particles] * [128 bytes] equals ... 8 MB. A whopping 8 MB of RAM.

There are printer drivers using more RAM on 32-bit XP machines.

If you have those particles inside an object class (and don't even use all the methods/data from that class), then yeah, you might end up with huge classes being repeated.

Also, ADDING a new element is as simple as incrementing the index and resetting all the data in that element. ZERO operating system memory allocations. ZERO system calls. ZERO context switches to kernel mode for the kernel driver (SLOW!). All you're doing is setting some values (a simple bit copy, if you're smart with your struct). It's orders-of-a-magnitude faster.

And best of all? When you pack them in an array you can operate on more than one at a time--which you should be. Cache line size of almost every CPU in the last two decades is 64 bytes. That's the minimum amount of RAM it can read at a time and if you change one byte, it still has to FLUSH every other byte in that cache line.

It's called "data oriented design" and every console game does it. If it works for Gamecubes and Xbox (originals), it'll work on a 2 GB RAM XP machine.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

Peter Hull
Member #1,136
March 2001

Looking at it, you've got sprite which has virtual methods animate and draw, then particle as a subclass of sprite, then further subclasses of particle (e.g. pTwilight). How about if you had a class particle_group as a subclass of sprite, with each particle group managing a whole collection of particles. Each particle would be a small, data-only object (size, velocity, colour). Each particle effect would be a subclass of particle_group (e.g. twilight_group). Its animate method would move all particles and its draw method would draw all particles.

The benefit is that you cut down the number of sprites dramatically whilst still maintaining the ability to treat all visual objects uniformly.

Does that make sense?

Audric
Member #907
January 2001

Note that each particle has its own lifespan. When a particle needs be removed in the middle of the array, displacing every particle after it one step up would be expensive. An alternative is to pick the last particle of the array, put it in the slot that you want to free, and then decrement the array's "top counter".
This does un-sort the array over time, though, so hopefully the rendering engine doesn't need particles to stay ordered the same way.

Go to: