Heap


#1

IMHO JUCE uses dynamic allocation too frequently. It makes thousands calls to operator new, juce_malloc and juce_realloc per second, allocating (and freeing) hundreds megabytes per minute, even if application is idle. Don’t speaking about heap fragmentation and waste of CPU time, I see following problem:

MainDemoWindow::~MainDemoWindow uses JUCEApplication::quit.
JUCEApplication::quit internally uses new Message(…). If operator new throws… http://www.parashift.com/c+±faq-lite/exceptions.html#faq-17.3


#2

It’s only a demo - you don’t have to call quit() from a desctructor if you don’t want to.

I’ve always gone with the principal that if new() throws an exception, then you probably can’t move the mouse or see any windows any more, you’ve probably tried pressing ctrl-alt-delete with no results, and you’re most likely reaching for the power switch. So it’s not such a big deal what happens to the app. I admit this isn’t a great attitude for a life-support machine or jumbo jet control system, but if you’re writing one of those, please don’t use juce!

Are there any places you’ve spotted where these allocations aren’t needed? Most of it will be messages, which need to be sent, and which can’t be stack based, so there’s no easy alternative I can see.


#3

We just hope to see proper demo in next version of juce.

Depends upon mouse driver. If you decide write your own mouse driver it will probably not work in low memory condition.

I see nearly no places in application code where allocations are strictly needed (at least while application in idle)! Messages? I am wondering why http://lonecoyote.home.mindspring.com/basic-app.c doesn’t allocate on every message? Application processes one message at a time, so why allocating? In PathStrokeType::createStrokedPath line l = new LineSection(); also called too frequently, you may use hightly optimized STL containers instead of horrible JUCE::Array. Do you think this code is clever:

const Array<ElementType>& operator= (const Array<ElementType>& other) throw()
    {
        if (this != &other)
        {
            other.lock.enter();
            lock.enter();

            this->granularity = other.granularity;
            ensureAllocatedSize (other.size());
            numUsed = other.numUsed;
            memcpy (this->elements, other.elements, this->numUsed * sizeof (ElementType));
            minimiseStorageOverheads();

            lock.exit();
            other.lock.exit();
        }

        return *this;
    }

even JUCE::Array::add is able to break nearly every class! calling operator= on uninitialized memory! Very nice!


#4

Sorry, just to ask
Where is unintialized memory ?

Look inside STL code, and you’ll see what insane dynamic allocation is.
Try to = two vector, or if you want to compare the code size (by using specialized containers), try to compare the code needed to simply add /remove 20 elements in Juce and in STL (without even speaking of STL incompatibilities, bugs).

Come on, it’s much, much easier (and, in the long term, more efficient), to maintain a clear Juce code than a STL’ed templated spaghetti mess code.

Okay, your code is great, but it doesn’t even do 1/100th of what Juce does. Compare chicken with chicken please.

Sincerely,
Cyril


#5

We just hope to see proper demo in next version of juce.

Depends upon mouse driver. If you decide write your own mouse driver it will probably not work in low memory condition.

I see nearly no places in application code where allocations are strictly needed (at least while application in idle)! Messages? I am wondering why http://lonecoyote.home.mindspring.com/basic-app.c doesn’t allocate on every message? Application processes one message at a time, so why allocating? In PathStrokeType::createStrokedPath line l = new LineSection(); also called too frequently, you may use hightly optimized STL containers instead of horrible JUCE::Array. Do you think this code is clever:

const Array<ElementType>& operator= (const Array<ElementType>& other) throw()
    {
        if (this != &other)
        {
            other.lock.enter();
            lock.enter();

            this->granularity = other.granularity;
            ensureAllocatedSize (other.size());
            numUsed = other.numUsed;
            memcpy (this->elements, other.elements, this->numUsed * sizeof (ElementType));
            minimiseStorageOverheads();

            lock.exit();
            other.lock.exit();
        }

        return *this;
    }

even JUCE::Array::add is able to break nearly every class! calling operator= on uninitialized memory! Very nice![/quote]

It’s easy to look through any library and find a few criticisms, but are any of the things you mention here actually causing you real problems? Would they ever cause anyone any problems? I can’t imagine how.

Does the fact that new() gets called when your app is idle mean that it fails to run correctly? Is createStrokedPath too slow for you because it spends an extra microsecond creating an object? If so, I’d love to hear what it is you’re trying to write!


#6

Probably you can override your version of JUCE news to use slot-pools for all calls of new that are the size of the message class (or something like taht). I mean Jules could probably also do that, but it might be not the best thing to do, if you are running as a DLL (e.g. you are not the host, you are being hosted) - and I believe most of the JUCE users are using it that way (e.g. some forms of VST plugins, etc.).

just my 2 cents.

I for example have local changes to my JUCE, and I do them everytime I get the latest version of JUCE, as remove any #pragma comment(lib,…) - even that you have predefined symbol DONT_INCLUDE_etc… I’m bit against that (do not like it, as I have to defined that symbol everytime I use JUCE, I don’t like much -Dxxx staying for no reason). Also juce.h never does “using namespace juce” in my local version. ZLIB/PNG/JPG are not the juce versions, but what I have on my machine, my folder. Also JUCE does not include them in my version too, but uses them externally… And few other things, but those are my local changes, and I’m not sure whether they’ll serve good purpose for the rest of the people. I do not, and do not plan to use JUCE as DLL hosted application, but rather as a separate executable - for small/medium utility tools.

What I’m saying is that if you don’t like something, you could aswell try to fix it (I’ve also fixed (kind of) one of the filler algorithms - the radial one - it was too slow, but still buggy sometimes in my version) and if Jules feels like the changes are good, he might take them, and put them in JUCE.


#7

That’s quite a few mods you’ve got going! If you think of anything I could do that’d help you set it up then let me know. Adding #defines to the config are easy enough.


#8

Seems like it would be fairly straightforward to implement a reusable message pool; you’d just have to add a custom new and delete operator to Message that only allocated new memory when the pool was exhausted. And, of course, some way to clean up when you quit.

There is the low-fragmentation heap in Win2K & XP, but that doesn’t help on the other platforms.


#9