News for hackers. Stuff that matters.
Re:Assault by GC by lucisferre on Monday October 24, 2011 @11:18AM (#3149962)
This solution seems extreme, given the GC is just a reality of working with managed languages which is most of the web these days (.NET, Java, Ruby, etc.). Many, many heavy traffic websites solve these problems without resorting to language magic tricks. Usually with effective use of caching, or separating out the heavily accessed data in a way that is is optimized for those read operations.

This is the second StackExchange article where it feels like square peg, round hole. The first was [their ORM] (http://samsaffron.com/archive/2011/03/30/How+I+learned+to+st...) which isn't really an ORM at all (perhaps micro ORM is fair) in the traditional sense.

Re:Assault by GC by ajross on Monday October 24, 2011 @04:52PM (#3151529)
More often they do it just by using non-garbage-collected heap strategies. PHP, Perl, Python and (I believe) Ruby all use reference-counted allocators and don't exhibit the kind of latency explosions you see with GC algorithms.
Re:Assault by GC by kmontrose on Monday October 24, 2011 @05:13PM (#3151612)
Pure reference counting doesn't handle cycles. I believe those languages (and I'm pretty certain Python does) implement a periodic mark-and-sweep GC for those cycles.

So you still periods of high latency collections (in .NET only Gen2 collections are actually noticeable, not sure about other platforms). You also have the overhead of managing reference counts, which can be non-trivial; especially if you're paging as a result.

Of course, comparing GC performance between C# and Python or Ruby is sort of silly. If we were trying to hit our ~40ms Question/Show render targets on them we'd probably have lower hanging fruit than GC.

Re:Assault by GC by mst on Monday October 24, 2011 @05:31PM (#3151692)
Perl doesn't have mark-and-sweep - instead you use weak references to prevent cycles causing leaks. The combination of Devel::Gladiator and Devel::FindRef allow for manual mark-and-complain for debugging, which tends to work quite well.

It's also possible in the case of object structures to have a sort of "ring" where the weak reference gets shuffled around said ring in order to allow a safe cycle without expecting the user of said objects to care (the trick is that objects' DESTROY methods are allowed to make new references to the object, which lets you do the shuffling just before it would normally have been freed).

I don't entirely see how managing reference counts would cause paging - in perl they're stored as part of the C-level struct for the value so the data's already nicely local to the memory you're already accessing - and when a ref count hits zero the value gets put into a "clean this up when this VM op has finished executing" array so it's pretty efficient.

I would suspect this sort of potential problem is why ObjC seems to be looking towards reference counting - the "smoothness" of not having any chance of a GC pause is probably to apple's eyes a worthwhile trade-off for the disadvantages of reference counting.

Strikes me it might be interesting to be able to mark objects as reference counted as a halfway house between fully GC'ed and fully manual, but I'm not sure whether the amount of use that would get would justify the effort involved.

Re:Assault by GC by kmontrose on Monday October 24, 2011 @06:02PM (#3151821)
Haven't done much Perl hacking, good to know it's more-or-less pure reference counting.

Paging can occur as a consequence of copying a reference.

    var p1 = *A  // let p1 be a reference to A
    // something in the interim
    var p2 = p1  // create a new reference to A via p1
If A is no longer paged in incrementing its reference count (as a consequence of copying p1 into p2) can cause a page fault. There's are also gotchas with destroying references such as a collection having to effectively iterate over its contents to decrement each reference count, again possibly leading to additional paging.

This assumes an object carries its reference count with it, which every reference counting system I'm aware of does. I suppose you could technically keep reference counts in a common space, though growing that would be a bit rough.

A GC isn't a magic bullet or anything, but they don't need to cause any paging as a result of reference changes until a sweep; and even then, they only need to page in the live set which is more likely to be resident in memory.

I'm... skeptical Apple's introduction of ARC is really performance oriented. Seems more like they tried and failed to build a workable GC for Objective-C with Tiger. Haven't worked with much Objective-C since 10.4 though, so I'm not completely settled either way.

Re:Assault by GC by mst on Monday October 24, 2011 @10:08PM (#3152485)
Ah, the copying part, yes - but then again if I'm doing that, I'd expect to be doing so in order to access A at which point I already needed that bit of memory.

The collection thing I can definitely see though.

One thing I've never quite worked out is - without reference counting, how do you handle timely destruction?

I know that if I do something like:

  {
    my $x = foo();
    ...
  }
then $x will be destroyed at the exact point the } is hit, or that an exception unwinds the stack past this point. That lets me do all sorts of nice things like rolling back a transaction automatically, throwing a warning if some task was expected to be completed but hasn't been, and other bits and pieces.

In a mark-and-sweep GC language, presumably there's some other additional feature that needs to be implemented in order to provide this?

Re:Assault by GC by kmontrose on Tuesday October 25, 2011 @12:05AM (#3152773)
Well, there are certainly cases where you grab a reference speculatively; anything where you need > N in a collection to act on, for instance, but don't know how many items there are yet (such as when you're reading from a stream). There's also the "early return" case, where you'd grab reference say A, B, and C but only dereference C in code that runs if A and B failed some condition.

The equivalent to timely destruction in C# is a using statement combined with implementing IDisposable.

    using(var stream = File.Open(somePath))
    {
       // use stream to do whatever
    }
This is syntactic sugar for a try{ } finally { } (that also exists in Java) that calls Dispose() in finally. You do any cleanup you need to in Dispose(), things like releasing file handles. The only way to dodge that Dispose() is to tear the execution thread down, which generally means the process is gone with it.

C# classes (but not structs) can also declare destructors, but their execution schedule isn't guaranteed; they're useful for making absolutely sure resources are released eventually, Dispose() is preferred for timing and performance.

Re:Assault by GC by ajross on Monday October 24, 2011 @05:24PM (#3151665)
No, they just fiat "cycles" in as a possible memory leak against which the app developer has to guard. That's not so bad, really: it's far, far easier than manually freeing memory. And in any case there are all sorts of resource leaks that GC can't find anyway; this is minor in comparison.

Your last point seems flat wrong, btw. If you're in a GC environment with worst-case latency requirements (real requirements, not just nice-to-haves or 99.9th percentiles, or whatnot) of 40ms, then you're in a whole world of hurt. GC won't do that -- brush up on your C.

Re:Assault by GC by kmontrose on Monday October 24, 2011 @05:41PM (#3151740)
A memory leak is pretty terrible, and having to either a) disallow cycles or b) specially handle them in user code is also pretty terrible IMO. Loads of handy graph data-structures, all sorts of fun caching techniques just got either impossible or much more difficult. You definitely can work with those systems but it's like stepping into a time machine (one spitting you out around '94 at an MS COM presentation), why make do unless you have to?

I never claimed GC's solve all leaks either (they do a pretty good job with finalizer semantics honestly), but they're awfully awfully handy for solving very common (and sometimes tricky) ones.

I also did not mean to imply that our 40ms cut is a hard limit, it's a target average with a desire to minimize variability (Marc's work on which lead to this post); one that we're hitting pretty well. If you're working on a real-time system, you're right that you shouldn't be using these non-deterministic GCs.

Re:Assault by GC by dmpk2k on Monday October 24, 2011 @07:43PM (#3152112)
They're not mainstream (that I'm aware of), but there are realtime GCs that can guarantee a pause of less that 1ms. Metronome is the one I'm familiar with.
Re:Assault by GC by ajross on Tuesday October 25, 2011 @11:40AM (#3154417)
True enough, there are research systems that have been built with hard realtime properties. I don't know much about them. So I should have qualified it with "all popular GC-based interpreters" or the like.
Re:Assault by GC by sams99 on Tuesday October 25, 2011 @04:13AM (#3153277)
I am really not following your criticism of Dapper? Without Dapper we would both need more servers AND have significantly higher latency.

There is no magic here, it is merely understanding the limitations of the .NET GC and adapting to it. XNA and trading platforms also are aware of these issues and have to follow similar patterns.