July 26, 2016
On Tuesday, 26 July 2016 at 01:07:27 UTC, Chris Wright wrote:
>
> @nogc conflates "doesn't allocate memory from the GC heap because I don't want my application to use the GC at all" with "doesn't cause GC collection pauses".
>
> The latter can have a @assumenogc annotation that works -- you call GC.disable and GC.enable as appropriate. The former can't.

For whatever reason, I like the idea of annotations behaving like enums. So instead of @gc or @assumenogc, you could have something like @nogc.false or @nogc.assume. You wouldn't add new annotations, just new behavior for existing annotations.
July 26, 2016
On Tuesday, 26 July 2016 at 02:49:59 UTC, ketmar wrote:
> On Tuesday, 26 July 2016 at 02:42:52 UTC, bitwise wrote:
>> Something like @warngc could work nicely. It could function exactly as @nogc with the exception that it issued warnings instead of errors.
>
> i don't think that it will be added, though. but you can emulate it with external utility: either with dscanner (writing your own linter), or with "-vgc" and then using simple regexp to find in which function the reported line is in. somewhat messy, but it is the way to get the work done without waiting for new attr. ;-)

Actually, thinking about this a bit more, I think @warngc would be bad anyways. Code that was basically which worked exactly as is was designed to would issue errors, which would be bad.

Writing a custom linter is not an option. Not only because of the work/maintainence involved, but because it would complicate workflow. What I want is easily achieved during compilation.

My goal is to proactively deal with allocations that are in places that they shouldn't be 90% of the time, like per-frame updates in a real-time application or game.

    Bit
July 26, 2016
On Tuesday, 26 July 2016 at 14:51:21 UTC, jmh530 wrote:
> On Tuesday, 26 July 2016 at 01:07:27 UTC, Chris Wright wrote:
>>
>> @nogc conflates "doesn't allocate memory from the GC heap because I don't want my application to use the GC at all" with "doesn't cause GC collection pauses".
>>
>> The latter can have a @assumenogc annotation that works -- you call GC.disable and GC.enable as appropriate. The former can't.
>
> For whatever reason, I like the idea of annotations behaving like enums. So instead of @gc or @assumenogc, you could have something like @nogc.false or @nogc.assume. You wouldn't add new annotations, just new behavior for existing annotations.

Chris is right that @safe/@trusted is not analogous to @nogc/@assumenogc, but I still think @assumenogc is a good idea..or maybe @nogc(false) to offer the opposite behaviour of @nogc.

There are arguments that can be made against this idea, but IMO, it's really hard to screw this up unintentionally. It's not like it's an invisible implicit conversion.

There are plenty of valid use cases. One, like I mentioned, about strongly deterring allocations in certain contexts instead of completely disallowing them. Also, deferring garbage collection until the end of a performance sensitive algorithm that may still allocate, so that GC.collect can be called manually afterward.

   Bit.

July 27, 2016
I am not convinced this is the best way to go although it would be a relatively easy solution.

What I would personally prefer was if there was a language mechanism to mark theAllocator, processAllocator (iAllocator) to be @nogc as long as the allocator in question is not the GCAllocator.

I am thinking specifically of the situation where vendor A can create a dynamic library in D that uses theAllocator and vendor B uses that library without necessarily having the source code.


On Wed, Jul 27, 2016 at 7:05 AM, bitwise via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Tuesday, 26 July 2016 at 14:51:21 UTC, jmh530 wrote:
>>
>> On Tuesday, 26 July 2016 at 01:07:27 UTC, Chris Wright wrote:
>>>
>>>
>>> @nogc conflates "doesn't allocate memory from the GC heap because I don't want my application to use the GC at all" with "doesn't cause GC collection pauses".
>>>
>>> The latter can have a @assumenogc annotation that works -- you call GC.disable and GC.enable as appropriate. The former can't.
>>
>>
>> For whatever reason, I like the idea of annotations behaving like enums. So instead of @gc or @assumenogc, you could have something like @nogc.false or @nogc.assume. You wouldn't add new annotations, just new behavior for existing annotations.
>
>
> Chris is right that @safe/@trusted is not analogous to @nogc/@assumenogc, but I still think @assumenogc is a good idea..or maybe @nogc(false) to offer the opposite behaviour of @nogc.
>
> There are arguments that can be made against this idea, but IMO, it's really hard to screw this up unintentionally. It's not like it's an invisible implicit conversion.
>
> There are plenty of valid use cases. One, like I mentioned, about strongly deterring allocations in certain contexts instead of completely disallowing them. Also, deferring garbage collection until the end of a performance sensitive algorithm that may still allocate, so that GC.collect can be called manually afterward.
>
>    Bit.
>
July 27, 2016
On Sunday, 24 July 2016 at 22:13:02 UTC, bitwise wrote:
>
> There is the following, which is clever. But if it came down to having to do this to bypass @nogc, I simply wouldn't use @nogc.
>
> https://p0nce.github.io/d-idioms/#Bypassing-@nogc
>
> When you have to do it thousands of times throughout your codebase, then yes, it's that bad.
>

FWIW I've removed every use of that bypassing (was used for runtime initialization or semaphores locks).
You can also use emplace/destroy like in: https://github.com/d-gamedev-team/gfm/blob/master/core/gfm/core/memory.d#L238
July 28, 2016
On Wednesday, 27 July 2016 at 20:42:01 UTC, Guillaume Piolat wrote:
> On Sunday, 24 July 2016 at 22:13:02 UTC, bitwise wrote:
>>
>> There is the following, which is clever. But if it came down to having to do this to bypass @nogc, I simply wouldn't use @nogc.
>>
>> https://p0nce.github.io/d-idioms/#Bypassing-@nogc
>>
>> When you have to do it thousands of times throughout your codebase, then yes, it's that bad.
>>
>
> FWIW I've removed every use of that bypassing (was used for runtime initialization or semaphores locks).
> You can also use emplace/destroy like in: https://github.com/d-gamedev-team/gfm/blob/master/core/gfm/core/memory.d#L238

The point is though, that I WANT to use the GC. I want the memory cleaned up for me, and I don't mind little pauses once in a while. I just don't want careless allocations to happen in certain performance-sensitive contexts, like per-frame updates.

While working on a past project(C++), I found this little gem:

void draw() {
    Font* f = new Font("arial.ttf", 16);
    drawText(f, "hello world");
}

As utterly moronic as this seems, this was a real bug that I had to fix. Our game was literally topping out at 2GB of memory usage after ~30 seconds and crashing.

Note: it wasn't my code ;)

If that code was written in D with the feature I'm asking for, draw() would have been marked with @nogc. The person who wrote the above code would have either had to store the font somewhere else, or insert a @assumenogc{}  section to actually do that. So it either would not have happened, or would have been much easier to find.

If you found that your game/app was using too much GC, searching for "@assumenogc" would likely uncover the cause, as long as your root classes were annotated correctly. The @assumenogc annotation would plainly show where allocations were happening that maybe shouldn't be.

    Bit
    Bit



July 28, 2016
On Thursday, 28 July 2016 at 00:23:57 UTC, bitwise wrote:
> The point is though, that I WANT to use the GC. I want the memory cleaned up for me, and I don't mind little pauses once in a while. I just don't want careless allocations to happen in certain performance-sensitive contexts, like per-frame updates.
>
> While working on a past project(C++), I found this little gem:
>
> void draw() {
>     Font* f = new Font("arial.ttf", 16);
>     drawText(f, "hello world");
> }
>
> As utterly moronic as this seems, this was a real bug that I had to fix. Our game was literally topping out at 2GB of memory usage after ~30 seconds and crashing.
>
> Note: it wasn't my code ;)
>
> If that code was written in D with the feature I'm asking for, draw() would have been marked with @nogc. The person who wrote the above code would have either had to store the font somewhere else, or insert a @assumenogc{}  section to actually do that. So it either would not have happened, or would have been much easier to find.
>
> If you found that your game/app was using too much GC, searching for "@assumenogc" would likely uncover the cause, as long as your root classes were annotated correctly. The @assumenogc annotation would plainly show where allocations were happening that maybe shouldn't be.

If @assumegc has to be manually checked to see if it is over-allocating whenever the application is going out of memory, then it is no more useful than running the gc-profiler when the application is over-allocating. The profiler is even better, because with @assumegc you have to check ALL marked functions to find the leaking one, while the gc-profiler would just put it on top of the report, making your job easier. So IMHO we already have an instrument to solve these problems.
July 28, 2016
On Thursday, 28 July 2016 at 07:12:06 UTC, Lodovico Giaretta wrote:
> On Thursday, 28 July 2016 at 00:23:57 UTC, bitwise wrote:
>> The point is though, that I WANT to use the GC. I want the memory cleaned up for me, and I don't mind little pauses once in a while. I just don't want careless allocations to happen in certain performance-sensitive contexts, like per-frame updates.
>>
>> While working on a past project(C++), I found this little gem:
>>
>> void draw() {
>>     Font* f = new Font("arial.ttf", 16);
>>     drawText(f, "hello world");
>> }
>>
>> As utterly moronic as this seems, this was a real bug that I had to fix. Our game was literally topping out at 2GB of memory usage after ~30 seconds and crashing.
>>
>> Note: it wasn't my code ;)
>>
>> If that code was written in D with the feature I'm asking for, draw() would have been marked with @nogc. The person who wrote the above code would have either had to store the font somewhere else, or insert a @assumenogc{}  section to actually do that. So it either would not have happened, or would have been much easier to find.
>>
>> If you found that your game/app was using too much GC, searching for "@assumenogc" would likely uncover the cause, as long as your root classes were annotated correctly. The @assumenogc annotation would plainly show where allocations were happening that maybe shouldn't be.
>
> If @assumegc has to be manually checked to see if it is over-allocating whenever the application is going out of memory, then it is no more useful than running the gc-profiler when the application is over-allocating. The profiler is even better, because with @assumegc you have to check ALL marked functions to find the leaking one, while the gc-profiler would just put it on top of the report, making your job easier. So IMHO we already have an instrument to solve these problems.

It's not about running out of memory. It's a performance issue.

Example: In the following class, it would be perfectly fine, and even expected to allocate in start() but not in update(). This is because start() gets called once on object initialization, but update() would get called every frame.

class NPC {
    // ....
    void start() { this.hat = new Hat(); }
    void update() @nogc {
        this.timer += Clock.currTime;
        check(this.timer);
        updateUI();
    }
}

Finally, the use of a profiler, and @nogc/@assumenogc wouldn't be mutually exclusive. If a profiler had a filter-by-attribute functionality, you could set it to @nogc functions, and it would narrow down the results to the functions where allocations actually matter.

    Bit


July 28, 2016
On Thursday, 28 July 2016 at 15:24:10 UTC, bitwise wrote:
>
> It's not about running out of memory. It's a performance issue.
>
> Example: In the following class, it would be perfectly fine, and even expected to allocate in start() but not in update(). This is because start() gets called once on object initialization, but update() would get called every frame.
>

Vladimir implemented counting in -profile=gc, it gives you the number of allocations and the amount allocated for each allocation point.
July 28, 2016
On Thursday, 28 July 2016 at 16:15:04 UTC, Guillaume Piolat wrote:
> On Thursday, 28 July 2016 at 15:24:10 UTC, bitwise wrote:
>>
>> It's not about running out of memory. It's a performance issue.
>>
>> Example: In the following class, it would be perfectly fine, and even expected to allocate in start() but not in update(). This is because start() gets called once on object initialization, but update() would get called every frame.
>>
>
> Vladimir implemented counting in -profile=gc, it gives you the number of allocations and the amount allocated for each allocation point.

I'm not sure if this point is meant to be for or against what I'm suggesting, but still, I'm thinking in terms of *proactively* writing efficient code, not allowing an app get to the point where you need to run a profiler.

The situation is similar to using @safe. You *could* just write code however you want, wait until it crashes, then try to find the problem using Valgrind or something, but that's bad for obvious reasons.

    Bit