April 18, 2014
On Friday, 18 April 2014 at 06:16:29 UTC, Ola Fosheim Grøstad wrote:
> But how can you let the compiler know that you have protected the array so only one thread will take processing-ownership during the life span of the function call?

Btw, Rust apparently uses ARC for immutable data only.

April 18, 2014
On 4/17/2014 11:50 PM, Brad Anderson wrote:
> But surely something like:
>
> struct S
> {
>       this(int d) { data = d; }
>       S opBinary(string op)(S rhs) @nogc
>       {
>         return S(mixin("data "~op~" rhs.data"));
>       }
>
>       private int data;
> }
>
> Would still work, right? There is no GC activity there.

Right, because there is no gc activity!
April 18, 2014
On Friday, 18 April 2014 at 01:07:40 UTC, bearophile wrote:
> Kapps:
>
>> That code is not @nogc safe, as you're creating a dynamic array within it. The fact that LDC2 at full optimizations doesn't actually allocate is simply an optimization and does not affect the design of the code.
>
> I've added the opposite of what you say in the DIP. So Walter can fix it if it's wrong, or leave it there if it's right, because that DIP can't miss to to specify one behavour or the other:
> http://wiki.dlang.org/DIP60
>
> Bye,
> bearophile

Flags such as -O are specifically not supposed to change program behaviour. This being the case would completely discard that and allow code to be compiled only with a single compiler. Honestly, I think expecting that code to be allowed to use @nogc is a huge mistake and disagree with editing the DIP to include this solely because you decided it should.
April 18, 2014
Kapps:

> Flags such as -O are specifically not supposed to change program behaviour. This being the case would completely discard that and allow code to be compiled only with a single compiler.

OK.


> Honestly, I think expecting that code to be allowed to use @nogc is a huge mistake and disagree with editing the DIP to include this solely because you decided it should.

That Wiki page is editable, so if it's wrong it takes one minute to fix the text I have written. What I have decided to include is an explicit explanation regarding what a correct D compiler should do in that case.

Bye,
bearophile
April 18, 2014
On Friday, 18 April 2014 at 06:50:48 UTC, Brad Anderson wrote:
> On Friday, 18 April 2014 at 06:35:33 UTC, Walter Bright wrote:
>> On 4/17/2014 9:23 PM, Brad Anderson wrote:
>>> Would @nogc apply to code being evaluated at compile-time? I
>>> don't think it should.
>>
>> Yes, it would be. Compile time functions are not special, in fact, there is no such thing in D.
>
> But surely something like:
>
> struct S
> {
>      this(int d) { data = d; }
>      S opBinary(string op)(S rhs) @nogc
>      {
>        return S(mixin("data "~op~" rhs.data"));
>      }
>
>      private int data;
> }
>
> Would still work, right? There is no GC activity there.

I don't think that's a "compile time function" (hence the "there is no such thing in D"). Rather, it's a function called in a CTFE context. It *should* work, since currently, a safe, pure, nothrow function can call an impure, throwing unsafe one for CTFE.
April 18, 2014
On 17 April 2014 18:35, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 4/16/2014 8:13 PM, Manu via Digitalmars-d wrote:
>
>> On 17 April 2014 03:37, Walter Bright via Digitalmars-d
>> <digitalmars-d@puremagic.com <mailto:digitalmars-d@puremagic.com>> wrote:
>>     ARC has very serious problems with bloat and performance.
>> This is the first I've heard of it, and I've been going on about it for
>> ages.
>>
>
> Consider two points:
>
> 1. I can't think of any performant ARC systems.
>

Consensus is that there's no performant GC available to D either.
Can you show that Obj-C suffers serious performance penalty from it's ARC
system? Have there been comparisons?


2. Java would be a relatively easy language to implement ARC in. There's
> probably a billion dollars invested in Java's GC. Why not ARC?


Java is designed to be GC compatible from the ground up. D is practically
incompatible with GC in the same way as C, but it was shoe-horned in there
anyway.
Everyone that talks about fantasy 'awesome-GC's admits it would be
impossible to implement in D for various reasons.

I think Rainer's GC is a step forward; precision is definitely valuable, but benchmarks showed that it was slower. That didn't put anyone off though. I think it was generally agreed that precision trumped a small performance impact.


 Obviously, a critical part of ARC is the compilers ability to reduce
>> redundant
>> inc/dec sequences. At which point your 'every time' assertion is false.
>> C++
>> can't do ARC, so it's not comparable.
>>
>
> C++ has shared_ptr, with all kinds of escapes.


That's a library. The compiler knows nothing. Ref counting is useless without compiler support.


 With proper elimination, transferring ownership results in no cost, only
>> duplication/destruction, and those are moments where I've deliberately
>> committed
>> to creation/destruction of an instance of something, at which point I'm
>> happy to
>> pay for an inc/dec; creation/destruction are rarely high-frequency
>> operations.
>>
>
> inc/dec isn't as cheap as you imply. The dec usually requires the creation of an exception handling unwinder to do it.
>

Why do you need to do that?

The thing is, it's potentially 'workable'. It's also easy to rearrange and
factor out of hot code.
It's predictable and well understood.


 Have you measured the impact?
>>
>
> No. I don't really know how I could, as I haven't seen an ARC system.
>

Why do you have such a strong opposition if this is the case?


 I've never heard of Obj-C users complaining about the inc/dec costs.
>>
>
> Obj-C only uses ARC for a minority of the objects.
>

But you're always talking about how D creates way less garbage than other
languages, which seems to be generally true. It needs to be tested before
you can make presumptions about performance.
D offers some unique opportunities to improve on existing ARC
implementations, and combined with D's relative less garbage than other
languages, you might be surprised...


 How often does ref fiddling occur in reality? My guess is that with
>> redundancy
>> elimination, it would be surprisingly rare, and insignificant.
>>
>
> Yes, I would be surprised.
>

Well, I'd like to see it measured in practise. But most common scenarios I
imagine appear like they'd eliminate nicely.
pure, and perhaps proper escape analysis (planned?) offer great opportunity
for better elimination than other implementations like Obj-C.


     Further problems with ARC are inability to mix ARC references with
>> non-ARC
>>     references, seriously hampering generic code.
>> That's why the only workable solution is that all references are ARC
>> references.
>> The obvious complication is reconciling malloc pointers, but I'm sure
>> this can
>> be addressed with some creativity.
>>
>> I imagine it would look something like:
>> By default, pointers are fat: struct ref { void* ptr, ref_t* rc; }
>>
>
> First off, now pointers are 24 bytes in size. Secondly, every pointer dereference becomes two dereferences (not so good for cache performance).
>

That's not necessarily true. What length is the compiler typically able to
eliminate inc/dec pairs? How many remain in practise? We don't know.
The performance is to be proven. Under this approach, you'd bunch
references up close together, so there's a higher than usual probability
the rc will be in cache already.
I agree, it's theoretically a problem, but I have no evidence to show that
it's a deal breaker. In lieu of any other options, it's worth exploring.


 malloc pointers could conceivably just have a null entry for 'rc' and
>> therefore
>> interact comfortably with rc pointers.
>> I imagine that a 'raw-pointer' type would be required to refer to a thin
>> pointer. Raw pointers would implicitly cast to fat pointers, and a
>> fat->thin
>> casts may throw if the fat pointer's rc is non-null, or compile error if
>> it can
>> be known at compile time.
>>
>
> Now we throw in a null check and branch for pointer operations.
>

Well the alternative is to distinguish them in the type system. Without
making any language changes, I guess it's a requirement.
It would be completely predictable though, so it only amounts to a couple
of cycles.


 Perhaps a solution is possible where an explicit rc record is not required
>> (such
>> that all pointers remain 'thin' pointers)...
>> A clever hash of the pointer itself can look up the rc?
>> Perhaps the rc can be found at ptr[-1]? But then how do you know if the
>> pointer
>> is rc allocated or not? An unlikely sentinel value at ptr[-1]? Perhaps the
>> virtual memory page can imply whether pointers allocated in that region
>> are ref
>> counted or not? Some clever method of assigning the virtual address space
>> so
>> that recognition of rc memory can amount to testing a couple of bits in
>> pointers?
>>
>> I'm just making things up,
>>
>
> Yes.
>

I'm sure there are plenty of cool/clever tricks that may help.


 but my point is, there are lots of creative
>> possibilities, and I have never seen any work to properly explore the options.
>>
>
> ARC has been known about for many decades. If you haven't seen it "properly explored", perhaps it isn't as simple and cost-effective as it may appear at first blush.
>

I didn't say it was simple or cost effective. I don't know. I'm asking, is
it *possible* and would it work well?
I want to know why it's not possible, and if it is, then promote
exploration as a potential solution in D.


 So then consider ARC seriously. If it can't work, articulate why. I still
>> don't
>> know, nobody has told me.
>> It works well in other languages, and as far as I can tell, it has the
>> potential
>> to produce acceptable results for _all_ D users.
>>
>
> What other languages?
>

Well, Apple are the obvious demonstration that I'm familiar with. I haven't worked with any others, but they have been raised by other people in prior threads.


 iOS is a competent realtime platform, Apple are well known for their
>> commitment
>> to silky-smooth, jitter-free UI and general feel.
>>
>
> A UI is a good use case for ARC. A UI doesn't require high performance.
>

Nothing that requires high-frequency performance is a good case for managed
memory at all. You can't invoke a system allocator no matter how it's
implemented at the sort of frequency I think you're suggesting.
Apple demonstrate that direct application of ARC results in silky smooth
performance. That's hard to do, and it's throughout all user facing API's,
not just UI, so it's clearly not inhibiting that goal substantially.
I've haven't heard iOS programmers complain about it. I have heard Android
programmers complain about the GC extensively though.

Like I say, programmers would quickly learn the patterns (since they are predictable and reliable), and it appears that D offers substantially more opportunity for effective ref-fiddling-elimination than Obj-C.

 Okay. Where can I read about that? It doesn't seem to have surfaced, at
>> least,
>> it was never presented in response to my many instances of raising the
>> topic.
>> What are the impasses?
>>
>
> I'd have to go look to find the thread. The impasses were as I pointed out here.
>

I don't feel like you pointed any out. Just FUD.


 I'm very worried about this. ARC is the only imaginary solution I have
>> left. In
>> lieu of that, we make a long-term commitment to a total fracturing of
>> memory
>> allocation techniques, just like C++ today where interaction between
>> libraries
>> is always a massive pain in the arse. It's one of the most painful things
>> about
>> C/C++, and perhaps one of the primary causes of incompatibility between
>> libraries and frameworks. This will transfer into D, but it's much worse
>> in D
>> because of the relatively high number of implicit allocations ('~',
>> closures, etc).
>>
>
> There are only about 3 cases of implicit allocation in D, all easily avoided, and with @nogc they'll be trivial to avoid. It is not "much worse".
>

But they are fundamentally useful and convenient features though. I don't
want to have to emplace policy to ban them.
It separates a subset of D users as second class citizens that can't enjoy
the modern features of the language, and restricts their access to
libraries.

The thing is, these policies must become system-wide. GC must be banned everywhere to have any effect. Under a GC, even the low frequency code loses these conveniences offered by the D language, and you're creating a situation where libraries become very hard to trust.


 Frameworks and libraries become incompatible with each other, which is a
>> problem
>> in C/C++ that modern languages (java, C#, python, etc) typically don't
>> suffer.
>>
>
> A GC makes libraries compatible with each other, which is one reason why GCs are very popular.
>

Correct, and ARC as a form of GC would equally be applicable everywhere. That's my point. By sticking with a crappy GC, you're isolating an (important?) subset of D users into a world where we suffer annoying C++ patterns yet longer, and still can't depend on interoperability with useful libraries. These are critical failings of C++ by my measure. And foundational to my attraction to D in the first place.

I don't feel like you've given me any evidence that ARC in not feasible.
Just that you're not interested in trying it out.
Please, kill it technically. Not just with dismissal and FUD.


I fear that @nogc is a sort of commitment to the notion that the GC is here
to stay, will never be improved or changed, and that we are being boxed
into a world no different from C++. I'm not here because I want C++ with
better syntax, I'm here because I want to encourage the language that best
embodies the future of my industry.
I've put decades into manual memory management. I'm tired of it. So are all
the other game devs. Demonstrably so - many of whom are toying with C# and
other languages; which may be compatible with modern casual games, but not
compatible with major titles, or console/embedded titles. Nobody wants to
work with C++ anymore.


April 18, 2014
On 18 April 2014 16:16, via Digitalmars-d <digitalmars-d@puremagic.com>wrote:

> On Friday, 18 April 2014 at 00:11:28 UTC, H. S. Teoh via Digitalmars-d wrote:
>
>> I thought that whole point of *A*RC is for the compiler to know when ref count updates can be skipped? Or are you saying this is algorithmically undecidable in the compiler?
>>
>
> Multithreading cause major problems. A function owns the array passed as a parameter, no ref counting needed, but if another thread is deleting objects in the array then you cannot assume that ownership is transitive and will have to inc/dec every object you look at. If it is thread local then ownership is transitive and no inc/decs are needed in the function...?
>
> But how can you let the compiler know that you have protected the array so only one thread will take processing-ownership during the life span of the function call?
>

D pointers are thread-local by default, you need to mark things 'shared' explicitly if they are to be passed between threads. This is one of the great advantages D has over C/C++/Obj-C.


April 18, 2014
On 04/18/2014 10:50 AM, bearophile wrote:
>
>> Honestly, I think expecting that code to be allowed to use @nogc is a
>> huge mistake and disagree with editing the DIP to include this solely
>> because you decided it should.
>
> That Wiki page is editable, so if it's wrong it takes one minute to fix
> the text I have written.  What I have decided to include is an explicit
> explanation regarding what a correct D compiler should do in that case.

In which case? In case some version of LDC2 is able to avoid the heap allocation using full optimizations? :o)
April 18, 2014
On Friday, 18 April 2014 at 10:06:35 UTC, Manu via Digitalmars-d wrote:
> D pointers are thread-local by default, you need to mark things 'shared'
> explicitly if they are to be passed between threads. This is one of the
> great advantages D has over C/C++/Obj-C.

TLS sucks, as you get extra indirections if it isn't implemented using a MMU (typically not because the extra page tables would cost too much?). Besides, when I want globals it is for database like structures so I usually want them shared anyway.

There is a reason for why Rust only uses ARC for immutable data. But that could  be so useful that it might be worth it. E.g. for caches.

For efficient ARC I think you would need the equivalent of isolated threads and forbid pointers to internal data so that you can maintain ref-counting at the start of the object. Oth

Weak references is still an issue though. It takes extra effort. You either need a separate object, or you need a list of weak references to clear, or you will have to "realloc" the object (freeing the datasegment, but keeping the refcounting head, causing memory fragmentation), or some other "ugly" scheme. So you want whole program analysis to figure out for which objects you don't have to deal with weak references…

Ola.


April 18, 2014
On Thu, 17 Apr 2014 18:52:10 -0400, Walter Bright <newshound2@digitalmars.com> wrote:

> On 4/17/2014 3:18 PM, Steven Schveighoffer wrote:
>> During the entire processing, you never increment/decrement a reference count,
>> because the caller will have passed data to you with an incremented count.
>>
>> Just because ARC protects the data, doesn't mean you need to constantly and
>> needlessly increment/decrement references. If you know the data won't go away
>> while you are using it, you can just ignore the reference counting aspect.
>
> The salient point there is "if you know". If you are doing it, it is not guaranteed memory safe by the compiler. If the compiler is doing it, how does it know?

When I said you, I misspoke. I meant the compiler. If it isn't sure, it increments the count. But any objects passed into a function are already incremented. Basically it's like a mutex lock. You only need to increment at the most outer level of where you are using it.

This idea that every time you pass around a variable you need to adjust counts is just not true. This is not shared_ptr.

> You really are doing *manual*, not automatic, ARC here, because you are making decisions about when ARC can be skipped, and you must make those decisions in order to have it run at a reasonable speed.

Absolutely not, the compiler knows whether the count needs to be incremented, I don't need to know. In fact, in ARC, you are NOT ALLOWED to increment or decrement the count manually.

-Steve