Cast not your Perl before swine?

Bryan Sant bryan.sant at gmail.com
Tue Dec 18 08:44:59 MST 2007


On Dec 17, 2007 11:27 PM, Levi Pearson <levi at cold.org> wrote:
> You are clearly reading this from the wrong perspective.  These guys
> are supporters of garbage collection; they aim simply to measure what
> its cost is.  There's not a whole lot of quantitative research on the
> subject, because it's a hard thing to measure.  Pretty much all of the
> research does conclude that there is a runtime cost, though.  Most of
> it also agrees that although the time cost is minimal, the space cost
> of garbage collection can be significant.  This paper also shows that
> the time cost is dependant on having enough space for the collector to
> function efficiently.

I do understand that these researchers are supporters of GC, but their
conclusion is that GC requires at least 2x the amount of memory to be
comparatively efficient.  There's no doubt that GC has costs.
Reference counting and the other tracking goodies obviously isn't
free.  However, there are a number of ways that these costs are paid
for.  One that I've already mentioned is the cache friendliness.
Another big one is that GC will dramatically reduce the times an
application invokes malloc/free.  The fake application these guys
mocked likewise reduced the times their explicitly managed app calls
malloc/free to an unrealistic level.

I see the value that this study shows the GC community.  That of
providing a pure measurement of the costs of the (obsolete) GC
algorithms.  My objections is that this study falsely props up
explicit memory management as being more efficient than GC to an
unrealistic level.  I contend that the average medium to large sized
application written in C/C++ will waste more time with malloc/free
than the costs of GC.

> So?  That's largely irrelevant to the issue of garbage collection
> vs. explicit memory management.  They're presumably doing it that way
> because it's the environment they're equipped to instrument to get
> their numbers.

The study isn't claiming that you need more memory for GC.  They are
claiming that you need more memory to match the same *efficiency* as
explicit management.  So now we're talking about a performance
benchmark.  The environment that they've produce is so simulated and
abstracted away from true hardware that I can't fully trust their
performance findings.

> The locations where they simulated calling free weren't calculated at
> compile time, they were calculated at run time for a specific run of
> the application.  It's not a problem generally amenable to static
> analysis, or we wouldn't need garbage collectors in the first place!

Maybe I didn't fully understand their approach.  I thought they were
feeding "death records" from the GC version to a fake version that
would call a mocked out "free" call instead of the GC routine.

> Anyway, they do state that the manual allocation simulation is an
> optimal one, so there is some bias that way.  However, doing it that
> way is an interesting counterpoint to the typical route one uses to
> measure GC effects, which is to add a conservative GC to a standard

I agree that it does provide a theoretical best-case for explicit
memory management.

> C/C++ program and turn free() into a noop.  I feel this way is a
> better determination of the cost of GC in Java, since programs are
> written in Java style rather than C/C++ style.  Rewriting the programs
> would add a lot of uncontrolled variables to the study.l

I see what you're saying.  Let's compare Java GC with a theoretical
best-case malloc/free scenario to act as a baseline to help GC
algorithms become even more efficient.  However, I believe that
rewriting these apps in C/C++ would produce code that calls
malloc/free far more often and thus is less efficient with its memory
management that using a GC.

> Again, you're misinterpreting their intent.  It's not in question that
> decent explicit memory management nearly always has better time/space
> performance than garbage collection.  Modern GC alleviates some of the

That claim is my concern.  Modern GC is actually *faster* than a
typical C/C++ programs calls to malloc/free.  So the "time" portion of
your "time/space" claim is false in most cases.  The space part is
highly exaggerated by this study.  The GC will always need more space
than explicit management, but that amount is a fraction of what this
study suggests.

> Not everyone is out to bash Java and garbage collection.  These guys
> certainly weren't, and you don't need to feel compelled to get all
> defensive with them as you typically do with Java-bashing PLUGgers. :)

I know.  But most Java bashers are uninformed idiots.  You're actually
intelligent, so when you make a claim and back it up with a study, I
give it credence :-).

> The benefits of having data in contiguous memory are typically offset
> by the fact that doing liveness analysis tends to pull in stuff from
> all over the heap, blowing the cache out quite effectively.  This is
> especially painful when you have to pull in pages from swap just to
> check their pointers.

With a full GC this is true.  But not with an incremental GC which is
what Java and .NET and YARV (and I'm sure Python too) use.

> I haven't, but Cyclone, a C variant for embedded systems, uses
> region-based memory management for some memory.  It's also got a lot
> of other very nifty features.  I might use it in the future, but I
> haven't had occasion to yet.

Sweet.

Well, I just want to end this reply with a little experiment for all
to try.  Doing this contrived benchmark may better illustrate my
perspective.

<JAVA CODE>
public class CreateObject {
  public static void main(String args[]) {
    long start = System.currentTimeMillis();

    for (int n = 0; n < 10 * 1000 * 1000; n++) {
      new CreateObject(); new CreateObject();
      new CreateObject(); new CreateObject();
      new CreateObject(); new CreateObject();
      new CreateObject(); new CreateObject();
      new CreateObject(); new CreateObject();
    }

    long end = System.currentTimeMillis();
    System.out.println((end - start) / 1000.0 + " seconds");
  }
}
</JAVA CODE>

<C CODE>
#include <stdio.h>
#include <time.h>

int main(int argc, char **argv) {
  time_t start, end;
  int n;

  start = time(NULL);

  for (n = 0; n < 10 * 1000 * 1000; n++) {
    free(malloc(4)); free(malloc(4));
    free(malloc(4)); free(malloc(4));
    free(malloc(4)); free(malloc(4));
    free(malloc(4)); free(malloc(4));
    free(malloc(4)); free(malloc(4));
  }

  end = time(NULL);
  printf("%d seconds\n", (int) (end - start));
  return 0;
}
</C CODE>


For the lazy, here is my ouput:

Java:
0.679 seconds

C:
6 seconds

-Bryan



More information about the PLUG mailing list