Go without garbage collector

5,893 views
Skip to first unread message

Archos

unread,
Jun 17, 2012, 8:00:10 AM6/17/12
to golang-nuts
There are many projects where you could need complete control of
memory so C follows being the tool to use, but it would be nice if we
would have a "C" with the sintaxis of Go, its concurrency, library and
fast compilation; its name could be G.

Any language/system developer has thinked in creating a "version" of
Go without the garbage collector?

Jesse McNelis

unread,
Jun 17, 2012, 8:25:47 AM6/17/12
to Archos, golang-nuts
You can't have Go syntax without a garbage collector.
There are many previous threads of the mailing list explaining why.


--
=====================
http://jessta.id.au

nsf

unread,
Jun 17, 2012, 8:58:22 AM6/17/12
to golang-nuts
Yes. At some point I was thinking this is the right choice and the most
important part of the Go is its syntax. I wrote a prototype compiler
for a C-like language but with Go syntax:

https://github.com/nsf/krawl
http://nosmileface.ru/krawl/krawl_by_example.html

It was working at some point and there was a clang plugin for importing
C stuff and I even wrote a tetris game in it:

https://github.com/nsf/krawl/blob/master/examples/tetris.krl

But then when I started to think about all the Go features (methods,
goroutines, channels, slices, maps, interfaces, select, defer), slowly
I realized that the Go isn't just a syntax and you can't get near Go
experience without GC and its magnificent runtime. If you try to
implement most of the Go features, you need some form of automatic
memory management. C approach just doesn't work, believe me and syntax
alone doesn't make much difference.

Frankly speaking my current thought on that is that the most important
feature of Go is memory safety. Without memory safety everything else
doesn't matter. You don't need slices, because they exists to do bounds
checking. You don't need maps and channels and goroutines, because
managing memory for these is pain in the ass. You can't really
implement interfaces either. Having C with Go syntax is nice, but who
will pay the maintenance cost for that.

Oh and the library. Who needs a programming language without big, well
tested library? And for a language without GC you can't just take Go's
library, it must be different. Inventing a library for a language is
fucking hard, believe me on that too.

If you like Go and doesn't accept memory management policy (GC),
perhaps there is another thing to try as a compromise. Perhaps a Go
compiler/runtime suite that is easy to embed into a C application would
make sense. Something like a lightweight Go interpreter/JIT compiler.
People use lua in memory management critical applications aren't they?
I can imagine Go as a better lua alternative. Yes, you still write the
core in C or C++, but for high level stuff Go makes perfect sense.

All I can say. :)

unread,
Jun 17, 2012, 2:42:33 PM6/17/12
to golan...@googlegroups.com
On Sunday, June 17, 2012 2:58:22 PM UTC+2, nsf wrote:
On Sun, 17 Jun 2012 05:00:10 -0700 (PDT)
Archos <raul...@sent.com> wrote:

> There are many projects where you could need complete control of
> memory so C follows being the tool to use, but it would be nice if we
> would have a "C" with the sintaxis of Go, its concurrency, library and
> fast compilation; its name could be G.
>
> Any language/system developer has thinked in creating a "version" of
> Go without the garbage collector?

Yes. At some point I was thinking this is the right choice and the most
important part of the Go is its syntax. I wrote a prototype compiler
for a C-like language but with Go syntax:

https://github.com/nsf/krawl
http://nosmileface.ru/krawl/krawl_by_example.html

It was working at some point and there was a clang plugin for importing
C stuff and I even wrote a tetris game in it:

https://github.com/nsf/krawl/blob/master/examples/tetris.krl

But then when I started to think about all the Go features (methods,
goroutines, channels, slices, maps, interfaces, select, defer), slowly
I realized that the Go isn't just a syntax and you can't get near Go
experience without GC and its magnificent runtime.
 
If you try to
implement most of the Go features, you need some form of automatic
memory management.

This is true only in case you want the language to guarantee some degree of memory safety.

Explicit memory management (like C) seems possible for a Go-like language if you introduce a delete() or free() function. If the programmer writes "var m map; ... delete(m)" there are no problems for the run-time to execute this action (I am not talking about the cost of implementation). Same in respect to deleting a channel or some other Go object.

A problematic part is a closure that outlives the frame in which it was created. The programmer would need to write "delete(aClosure)" to free the memory associated with variables captured by the closure. Executing "delete(aClosure)" in the run-time shouldn't pose a problem. In other words, it seems doable.

Then there are Go expressions like "&a" which will allocate memory from heap and store the local variable "a" there. But this is equivalent to "new(A)", so the programmer needs to understand that "&a" is like "new(A)" and needs remember to write delete(&a) or delete(aPtr) to free the memory.

... so in my opinion, Go with explicit memory management seems possible.
 
C approach just doesn't work, believe me and syntax 
alone doesn't make much difference. 

In my opinion, an important feature of a programming language (of any programming language, except assembly languages) is whether the compiler is generating datatype information that can be used by functions in run-time libraries and/or by user code. This can help automate certain kinds of tasks, including memory deallocation.

unread,
Jun 17, 2012, 2:48:53 PM6/17/12
to golan...@googlegroups.com, Archos
On Sunday, June 17, 2012 2:25:47 PM UTC+2, Jesse McNelis wrote:
On Sun, Jun 17, 2012 at 10:00 PM, Archos <raul...@sent.com> wrote:
> There are many projects where you could need complete control of
> memory so C follows being the tool to use, but it would be nice if we
> would have a "C" with the sintaxis of Go, its concurrency, library and
> fast compilation; its name could be G.
>
> Any language/system developer has thinked in creating a "version" of
> Go without the garbage collector?

You can't have Go syntax without a garbage collector.

I wouldn't be so sure about it.
 
There are many previous threads of the mailing list explaining why.

They may be wrong.

nsf

unread,
Jun 17, 2012, 4:40:30 PM6/17/12
to golan...@googlegroups.com
On Sun, 17 Jun 2012 11:48:53 -0700 (PDT)
⚛ <0xe2.0x...@gmail.com> wrote:

> > You can't have Go syntax without a garbage collector.
> >
>
> I wouldn't be so sure about it.
>

Let me rephrase myself. When someone says "I want Go without garbage
collection" it means a person wants a feel he has with Go, but at the
same time without garbage collection. At least that's my case. I wanted
exactly that. And you can't have that. You can build a language similar
to Go without GC, but you won't get a feel of Go. At least, I couldn't
do it. And maybe it's kind of obvious, but when there is a need to
manage memory, that factor alone creates a different programmer mindset.
And in my opinion what Go does so well for a programmer is establishing
its own mindset that gives a very nice and smooth development process.
What we call "a feel of Go".

That's actually very same mistake that leads to talks like "where is my
feature X? I want feature X in your language". And the problem here is
that a language is not just a collection of features, it's a
composition of features. You can't just stick something in and make it
better (see C++) and you can't throw something out. Every feature
addition/removal affects the language as a whole, mutating it to a
different state. And in my opinion GC is a critical feature that allows
you to have memory safety and (well, let's put it that way) memory
safety is one of the major features in Go.

So.. think about it. "I want Go with templates" and "I want Go without
garbage collection" are very similar things. Both hide the desire of
improving/changing something without realization that this will affect
other areas dramatically.

And to make a summary: I tried that, I did that mistake thinking you
can build something out of Go just by taking parts you like and mixing
them in some weird way. I was stupid (to make it clear, I'm not
implying that anyone is). Hopefully what I said makes some sense.


Offtopic:

Btw. Thanks for your work on GC precision, I really hope those patches
will get into Go. One of the areas where I want to apply Go is desktop
applications. And for these you need a precise GC, because some desktop
apps have uptime measured in days or weeks (especially on geek's linux
machines) and you clearly don't want to get mozilla's firefox fame for
eating all the memory.

Archos

unread,
Jun 18, 2012, 4:28:00 AM6/18/12
to golang-nuts


On Jun 17, 9:40 pm, nsf <no.smile.f...@gmail.com> wrote:
> On Sun, 17 Jun 2012 11:48:53 -0700 (PDT)
>
I understand that cann't be created the full sintaxis that has Go,
although it would be being great if it could have, at least:

+ No header files so we would have fast compilation
+ Variable/constant declarations; iota
+ Same basic types
+ Control flow structures (for, switch, and if) should work like in Go
+ Line-ending semicolons optional
+ Type conversions must be made explicit
+ go and select control keywords, and channels to support concurrent
programming, if it's possible
+ Built-in types like maps, Unicode strings, array slices
+ Variadic functions
+ Multiple return values
+ Funtions literal and closures ?
+ Error handling

And for the library, only would be necessary the basic packages like
fmt, bytes, string, strconv, testing, path, path/filepath, debug.

With all that you would not get a Go without garbage collector, but
instead a new better sintaxis than C.

Note: it's just an idea since the language developers know if it would
makes sense.

Job van der Zwan

unread,
Jun 18, 2012, 7:37:15 AM6/18/12
to golan...@googlegroups.com
So you don't think there are many features in Go that could be used as a replacement to similar-but-clunky features in C? In other words: a syntactic Go-like "dialect" of C? Not a "Go without GC", but a "C with better expressiveness and less ambiguity". Wouldn't that on its own be a great improvement of C? 

Then you could write the core in that dialect ;). 

nsf

unread,
Jun 18, 2012, 8:20:30 AM6/18/12
to golan...@googlegroups.com
No. I'm pretty sure it's possible to create a better C. But there is
a big chance that it won't have a "Go feel". Then the question is, in
that case what will happen with the Go? You know the famous
http://xkcd.com/927 . That's what I'm afraid of. To create something
that is better than C, but less mature (won't be accepted by industry
for a large period of time), something that isn't as easy to write as
Go. Do we really need another something? Probably a man with more faith
in what he's doing can do that, not me definitely.

Another thing that I'm trying to make it clear. What are the real
intentions here? Is a person wants to have a Go with manual memory
management or a C with Go syntax. These two aren't the same. Because in
one case we're talking about "Go feel", which includes the joy of
having memory safe language and many other qualities impossible without
GC and in the other case we're talking about C with a better syntax and
module system. The question is, C exists for 40+ years, why these
obvious problems are left unnoticed? No one tries to fix C these days,
people keep inventing languages with many radical changes in comparison
to C and many questionable new features. Perhaps that's because it's
not worth it? Syntax alone doesn't give much advantages over what you
do in C and even module system doesn't add something critical. Nowadays
we don't have speed issues compiling C code, it's fast enough. C++
isn't, but that's a different story.

And answering to Archos' list of things:

> I understand that cann't be created the full sintaxis that has Go,
> although it would be being great if it could have, at least:
>
> + No header files so we would have fast compilation
> + Variable/constant declarations; iota
> + Same basic types
> + Control flow structures (for, switch, and if) should work like in Go
> + Line-ending semicolons optional
> + Type conversions must be made explicit
> + go and select control keywords, and channels to support concurrent
> programming, if it's possible
> + Built-in types like maps, Unicode strings, array slices
> + Variadic functions
> + Multiple return values
> + Funtions literal and closures ?
> + Error handling
>
> And for the library, only would be necessary the basic packages like
> fmt, bytes, string, strconv, testing, path, path/filepath, debug.
>
> With all that you would not get a Go without garbage collector, but
> instead a new better sintaxis than C.
>
> Note: it's just an idea since the language developers know if it would
> makes sense.

I may agree it sounds interesting, but the devil is in the details. For
example what if your new language will be slower than most of the C
implementations? What about compatibility with C? If we use Go-like
runtime where goroutines are possible we have a problem of a different
calling convention (no conventional preallocated stack). What if
concurrency with manual memory management doesn't work? And according
to Rob Pike it doesn't work, because they had a language with manual
memory management in the past and it didn't work very well (I believe
it was Alef? correct me if I'm wrong). Error handling? Do you mean
panics or error interface. Both approaches have an actual problem of how
would you handle their memory. Have you seen "closures" in C++? If we're
talking about C-like environment with no automatic object lifetime
management at all, it will be even more worse. I can't see a pointer
arithmetic in your list, unsafe low level language without memory
arithmetic is a waste of time. Explicit type conversions do not work
unless your intention is to have native libraries for everything or
bindings for C libraries. Using C interfaces directly with explicit
type conversions will be very painful.

And of course we need a library, but how would it look like? Have you
tried to imagine all these packages you mention without memory
management? Who allocates strings? Who is responsible for releasing the
memory? Have you seen C string libraries?

Anyways, it seems that I'm trying to convince you people with something.
But frankly my only intention is to share what I've discovered while
actually trying to do what you're talking about. There are reasons why
Go is as it is. There is a huge amount of thought put into that and the
process of getting to Go is as long as 20-40 years.

Kyle Lemons

unread,
Jun 18, 2012, 2:08:34 PM6/18/12
to Archos, golang-nuts
In addition to the other comments I will point out:

Where do you free something that might have been allocated in a switch/if clause?
 
+ Line-ending semicolons optional
+ Type conversions must be made explicit
+ go and select control keywords, and channels to support concurrent
programming, if it's possible

Where/when do you free closures you pass to the "go" keyword?  Where do you free a channel?
 
+ Built-in types like maps, Unicode strings, array slices

Where do you free a map?
 
+ Variadic functions

Where do you free the implicit slice created for a variadic call?
 
+ Multiple return values
+ Funtions literal and closures ?

Who is responsible for freeing a closure?
 
+ Error handling

How do you know, given an `error`, whether to free it and how to free it?  This goes for all interface values.

deat...@gmail.com

unread,
Feb 11, 2020, 6:55:48 PM2/11/20
to golang-nuts
What about #vlang ? https://vlang.io/

robert engels

unread,
Feb 11, 2020, 8:59:26 PM2/11/20
to deat...@gmail.com, golang-nuts
It’s been PROVEN that GC outperforms all manual memory management except in EXTREMELY isolated cases (very non-traditional allocation or deallocation patterns).

It’s all about constraints and tolerances.

You design a “system” that takes both into account - if not, you’re not engineering, you're guessing.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/165ebe92-362d-44f0-9ddb-2e152276b6fc%40googlegroups.com.

alex.be...@gmail.com

unread,
Feb 11, 2020, 11:32:31 PM2/11/20
to golang-nuts
Actually, it was not proven. And in practice manual memory management seems to be outperforming GC in majority of cases.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

robert engels

unread,
Feb 11, 2020, 11:54:29 PM2/11/20
to alex.be...@gmail.com, golang-nuts
Here is a paper from 2005 https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf that proves otherwise.

GC techniques have radically improved since then, some with hardware support, so much so that it is no longer a contest.

To reiterate though, if you don’t have dynamic memory management - which is essentially allocate and forget - that will “probably" be faster (many GC systems have an extra level of indirection).

You can write robust systems without dynamic memory, but it is very very difficult - beyond the skills of most developers.

So most developers resort to dynamic memory at some point - and once you do that - GC will crush your manual memory management techniques.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/c03420c5-d1b0-4c73-8a61-f4fa131018f9%40googlegroups.com.

robert engels

unread,
Feb 12, 2020, 12:00:41 AM2/12/20
to alex.be...@gmail.com, golang-nuts
I found a more recent academic paper that proves my conclusions: 


I am sure you can search and find many more, but the principles stated in the above are going to apply regardless.

Kevin Chadwick

unread,
Feb 12, 2020, 6:03:58 AM2/12/20
to golan...@googlegroups.com
On 2020-02-12 04:53, robert engels wrote:
> You can write robust systems without dynamic memory, but it is very very
> difficult - beyond the skills of most developers.

Interestingly, Global variables are often frouned upon and quite rightly on
computer systems (I include rpi/phones), despite being widely used in simpler
embedded applications. Yet Greenhills software (ghs.com) have a military
embedded OS that costs a fortune and who's marketing states it *ONLY* uses
global variables for guaranteed reliability. I am sure it's development caused a
heck of a lot of head scratching and perhaps it has 0 concurrency. In my mind,
controlled stack memory is still a must for sanity. Of course without
concurrency, judging dynamic memory use, isn't a difficult task anyway. It's a
lot easier to estimate stack usage though with buffer insurance.

Brian Candler

unread,
Feb 12, 2020, 7:34:12 AM2/12/20
to golang-nuts
On Wednesday, 12 February 2020 05:00:41 UTC, robert engels wrote:

It's a student paper.

The bit that caught my eye was results of simulation using real Java programs:

it’s been proven that the runtime performance of the best-performing garbage collector is competitive with explicit memory management when given enough memory. In particular, when garbage collection has five times as much memory as required, its runtime performance matches or slightly exceeds that of explicit memory management.

However, garbage collection’s performance degrades substantially when it must use smaller heaps. With three times as much memory, it runs 17% slower on average, and with twice as much memory, it runs 70% slower.

Brian Candler

unread,
Feb 12, 2020, 7:45:47 AM2/12/20
to golang-nuts
On Tuesday, 11 February 2020 23:55:48 UTC, deat...@gmail.com wrote:
What about #vlang ? https://vlang.io/



"(Work in progress) There's no garbage collection or reference counting. V cleans everything up during compilation. If your V program compiles, it's guaranteed that it's going to be leak free."

I don't think they've understood the problem at all. There is no indication of how they plan to deal with this.  Real-world programs allocate long-lived stuff on the heap.  If there is no GC, I believe they will end up re-inventing Rust's ownership/borrowing paradigm.

Robert Engels

unread,
Feb 12, 2020, 8:11:26 AM2/12/20
to Brian Candler, golang-nuts
Aren’t we all all “students” :) 

The conclusions you cite are from the 2005 paper. 

I’m sure I can find other more recent peer reviewed papers that draw the same conclusions as the “student” one. 

I don’t think it is necessary though. If you understand how malloc or tcmalloc and how modern GC works you’ll also know why it’s the case. Even with tcmalloc, highly concurrent dynamic memory systems are a problem without GC. A simple example, with Rust dealloc a large dynamic object graph vs a GC language- expensive vs cheap. 

On Feb 12, 2020, at 6:36 AM, Brian Candler <b.ca...@pobox.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Henrik Johansson

unread,
Feb 12, 2020, 8:24:56 AM2/12/20
to Robert Engels, Brian Candler, golang-nuts
Sure, writing these system in a non-GC language is harder but that's not really what is talked about here right?
There is a reason why databases are not really successful in Java for example. Caching software are predominantly in C/C++.
Beating highly tuned C/C++ is not something that a GC can do by itself. What it brings is convenience and frankly it's "good enough"
for most cases. We shouldn't pretend that GC is be all end all to both developer and runtime performance.

Robert Engels

unread,
Feb 12, 2020, 9:02:54 AM2/12/20
to Henrik Johansson, Brian Candler, golang-nuts
Most of that is because their codebase predates Java. There are more modern dbs like Cassandra that are in Java. Certainly Hadoop is probably the largest distributed database in the world and it’s written in Java. 

On Feb 12, 2020, at 7:24 AM, Henrik Johansson <dahan...@gmail.com> wrote:



Kevin Chadwick

unread,
Feb 12, 2020, 10:01:57 AM2/12/20
to golang-nuts
On 2020-02-12 13:23, Henrik Johansson wrote:
> We shouldn't pretend that GC is be all end all to both developer and runtime
> performance.

Frankly I think performance has a lot to answer for and likely why a secure C
that drops features and/or performance, has never become mainstream. A default
of slow with unsafe like go has would be nice in the embedded world. After all
hw acceleration or assembly often replaces C where performance is required.

As far as GO is concerned, for it's intended use cases. OOM avoidance rather
than performance is the concern, if anything. You can always throw more hardware
at performance and it already performs many times faster than python etc..

Kevin Chadwick

unread,
Feb 12, 2020, 10:07:28 AM2/12/20
to golang-nuts
On 2020-02-12 14:02, Robert Engels wrote:
> Most of that is because their codebase predates Java. There are more modern dbs
> like Cassandra that are in Java. Certainly Hadoop is probably the largest
> distributed database in the world and it’s written in Java.

Bound to use more cpu cycles and memory than a c equivalent. Of course postgres
is more capable.

https://blog.timescale.com/blog/time-series-data-cassandra-vs-timescaledb-postgresql-7c2cc50a89ce/

Henrik Johansson

unread,
Feb 12, 2020, 10:11:50 AM2/12/20
to Kevin Chadwick, golang-nuts
Well, Cassandra has a rewrite in C++ ScyllaDB hat performs many times better so that particular example isn't really helping the GC case.

I don't mind the GC myself but I hear the "GC is actually faster" often and it seems not to be true in the wild although I am sure theoretical cases can be envisioned.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Robert Engels

unread,
Feb 12, 2020, 12:06:33 PM2/12/20
to Henrik Johansson, Kevin Chadwick, golang-nuts
Note sure where you are getting that - their own report  https://www.scylladb.com/product/benchmarks/aws-i3-metal-benchmark/ is garbage as their rational is completely flawed in choosing less bare metal nodes (but far bigger) - they have exponentially reduced the communication costs... Additionally, running single Java nodes on the metal machine would perform far better. Also, the GC tuning is not designed for latency with max pauses of 500ms.

Re-run the tests on Shenandoah or Zing and see how they compare.

Henrik Johansson

unread,
Feb 12, 2020, 12:23:18 PM2/12/20
to Robert Engels, Kevin Chadwick, golang-nuts
It's beside the point and you are wrong but please rerun in any setup you want.

Anyway for "day job" I am fine with GC.

Marcin Romaszewicz

unread,
Feb 12, 2020, 12:50:08 PM2/12/20
to robert engels, alex.be...@gmail.com, golang-nuts
Your paper proves your conclusions given your assumptions :) When there is no GC runtime in a language, you are open to managing memory however you see fit, and there are lots of models which are more efficient than reference counted smart pointers or whatever. I've worked on many realtime systems in my life, and in those, we take a completely different strategy - preallocate everything, since you can't fragment memory and can't call out to sbrk() since either of those could knock you off the predictable path. When you have object pools of some kind, allocation and deallocation is stupendously fast, you modify a pointer in an array. Not allocating is a lot faster than the fastest allocator. Yes, I'm being kinda facetious here, but these papers assume that you have a programming model with a generic memory management system. Given that assumption, I can see how a GC language could be faster, but how appropriate is it when the memory footprint is fixed (such as on a game console) or when predictable performance is very important (realtime systems).

-- Marcin

Robert Engels

unread,
Feb 12, 2020, 1:09:40 PM2/12/20
to Marcin Romaszewicz, alex.be...@gmail.com, golang-nuts
Most embedded systems are highly modular with limited inputs and outputs. It is far easier to apply custom memory handling to the module. 

It’s is the larger controller application that is far more complex as it has to deal with all of the inputs BBC and outputs of every module. It is very hard to write these systems - that often include elaborate UIs in a fixed memory environment. 

The point being lost is that if you don’t do any dynamic memory handling you can turn off GC - then you are strictly comparing compiler efficiencies and again a dynamic JVM can out perform statically compiled apps - but that’s another discussion. 

On Feb 12, 2020, at 11:50 AM, Marcin Romaszewicz <mar...@gmail.com> wrote:



Jesper Louis Andersen

unread,
Feb 12, 2020, 1:14:59 PM2/12/20
to Henrik Johansson, Kevin Chadwick, golang-nuts
If I may make an observation here:

I think the vast majority of all programs benefit in productivity from a GC. In the sense, that the GC is an adequate solution at the least, and a more efficient solution in many cases, especially if you factor in development time. Managing memory manually, especially in concurrent settings, is going to take some attention to detail, and is likely to slow down how fast you can get a system working.

What you are likely to be looking at here is good old selection bias. The problems that doesn't fare well under a current generation of GCs are likely to bubble to the top of syndication sites, simply for the fact that they are interesting outliers.

My experience, over the last, say 20-40 years, is that GCs are slowly eating more and more of the cake. As they improve, it definitely converges toward more viability, not less. There will always be programs for which they fare badly. But as they are detected, so are GCs improved.



--
J.

Robert Engels

unread,
Feb 12, 2020, 1:32:18 PM2/12/20
to Jesper Louis Andersen, Henrik Johansson, Kevin Chadwick, golang-nuts
Very well said.

-----Original Message-----
From: Jesper Louis Andersen
Sent: Feb 12, 2020 12:13 PM
To: Henrik Johansson
Cc: Kevin Chadwick , golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

Henrik Johansson

unread,
Feb 12, 2020, 1:34:18 PM2/12/20
to Jesper Louis Andersen, Kevin Chadwick, golang-nuts
Absolutely but this has been said for a very long time already and we have still to see it.

I agree GC's are getting better and better and who knows what the future holds.

Until then I will gladly ignore the small loss of performance simply becausenit rarely matter for the program's I write.

alex.be...@gmail.com

unread,
Feb 12, 2020, 4:07:29 PM2/12/20
to golang-nuts
I'm very familiar with this paper. It's not the first one that uses oracular memory management for comparison, the earlier one used ML as its langauge.

The problem with these papers is that they're using very artificial benchmarks, not really representative of real workloads. They additionally use languages that are very heap-oriented, with very few value objects.

GCs also have not radically improved since then, if anything they are worse now in massively-parallel environment than on single-core CPUs of yore.

On Tuesday, February 11, 2020 at 8:54:29 PM UTC-8, robert engels wrote:
Here is a paper from 2005 https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf that proves otherwise.

GC techniques have radically improved since then, some with hardware support, so much so that it is no longer a contest.

To reiterate though, if you don’t have dynamic memory management - which is essentially allocate and forget - that will “probably" be faster (many GC systems have an extra level of indirection).

You can write robust systems without dynamic memory, but it is very very difficult - beyond the skills of most developers.

So most developers resort to dynamic memory at some point - and once you do that - GC will crush your manual memory management techniques.

Michael Jones

unread,
Feb 12, 2020, 4:36:40 PM2/12/20
to alex.be...@gmail.com, golang-nuts
To me it seems the issue of concurrency and dynamic ownership of memory are so deeply connected to Go’s programming methodology that the “no GC” comparison is biased. 

In particular, coding to do it yourself but as perfectly as the GC across many concurrent routines is hard. Doing it better than the GC is hard. Caution encourages use of the tuned GC. 

Agree with posts above: preallocation is fastest. Hard real time from the 80s lesson. 

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/465e2109-e0a5-4fdc-9dbf-5670eb73bfef%40googlegroups.com.
--
Michael T. Jones
michae...@gmail.com

unread,
Feb 12, 2020, 5:21:35 PM2/12/20
to golang-nuts
On Wednesday, February 12, 2020 at 12:55:48 AM UTC+1, deat...@gmail.com wrote:
What about #vlang ? https://vlang.io/

If compile-time GC is the only memory management model that exists in V, then it is impossible to implement in V any program requiring dynamic/runtime GC such as a Python interpreter because V would free the memory allocated by the Python code when the Python code completes execution, which for some Python codes, even some trivial ones, means that the Python code terminates abnormally due to an out-of-memory exception.

In Go, compile-time GC could be an extra compiler switch which, when enabled, causes Go codes to fail to compile in case the compiler cannot compute how to manage memory. For real-time apps this might be a useful feature, although some real-time apps might still need to selectively turn off automatic memory deallocation to achieve their constraints.

Robert Engels

unread,
Feb 12, 2020, 5:57:04 PM2/12/20
to alex.be...@gmail.com, golang-nuts
GCs have radically improved since then - at least in practical implementation.

Again, see G1, Metronome, Zing or Shenandoah - none of these were available in 2005.

(Or even Go's GC performance progression - but as I mentioned, in this particular test the lack of a generational collector is holding it back).

-----Original Message-----
From: alex.be...@gmail.com
Sent: Feb 12, 2020 3:06 PM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

I'm very familiar with this paper. It's not the first one that uses oracular memory management for comparison, the earlier one used ML as its langauge.

The problem with these papers is that they're using very artificial benchmarks, not really representative of real workloads. They additionally use languages that are very heap-oriented, with very few value objects.

GCs also have not radically improved since then, if anything they are worse now in massively-parallel environment than on single-core CPUs of yore.

On Tuesday, February 11, 2020 at 8:54:29 PM UTC-8, robert engels wrote:
Here is a paper from 2005 https://people.cs.umass.edu/~emery/pubs/gcvsmalloc.pdf that proves otherwise.

GC techniques have radically improved since then, some with hardware support, so much so that it is no longer a contest.

To reiterate though, if you don’t have dynamic memory management - which is essentially allocate and forget - that will “probably" be faster (many GC systems have an extra level of indirection).

You can write robust systems without dynamic memory, but it is very very difficult - beyond the skills of most developers.

So most developers resort to dynamic memory at some point - and once you do that - GC will crush your manual memory management techniques.
On Feb 11, 2020, at 10:31 PM, alex.b...@gmail.com wrote:

Actually, it was not proven. And in practice manual memory management seems to be outperforming GC in majority of cases.

On Tuesday, February 11, 2020 at 5:59:26 PM UTC-8, robert engels wrote:
It’s been PROVEN that GC outperforms all manual memory management except in EXTREMELY isolated cases (very non-traditional allocation or deallocation patterns).

It’s all about constraints and tolerances.

You design a “system” that takes both into account - if not, you’re not engineering, you're guessing.

On Feb 11, 2020, at 4:29 AM, deat...@gmail.com wrote:

What about #vlang ? https://vlang.io/

David Riley

unread,
Feb 12, 2020, 9:06:52 PM2/12/20
to Henrik Johansson, Kevin Chadwick, golang-nuts
> On Feb 12, 2020, at 10:10 AM, Henrik Johansson <dahan...@gmail.com> wrote:
>
> Well, Cassandra has a rewrite in C++ ScyllaDB hat performs many times better so that particular example isn't really helping the GC case.
>
> I don't mind the GC myself but I hear the "GC is actually faster" often and it seems not to be true in the wild although I am sure theoretical cases can be envisioned.

GC *can* be faster, but it depends on the workload. I don't believe in universals here.

If you have a lot of random allocations, and a GC/allocator that's designed for that, it's great. Go works well for random-access workloads; it tends to have some issues with database workloads, which have more linear allocations and a different object lifecycle. Check out the discussions on caching algorithms for dgraph for some very interesting points of view on that.

People resigned themselves to writing custom allocators for purpose-specific things in the game engine world in C/C++ a long time ago. In Go, you could do the same for specific use cases if you wanted as well, though you'd lose the advantages of GC (though I'd argue that in a database, if you can't track the objects you own, you're in for a world of hurt anyway).

Interacting with the allocator, as well as OS-level virtual memory structures, has long been a facet of database and similar design in the C/C++ world, and it's only escapable in the higher-level language world (e.g. Go, Java) if you want to avoid any discussion of performance issues. If you haven't had to consider structures like B-trees and B-heaps to optimize for various virtual memory scenarios, you haven't delved deeply enough into the problem space to be talking about whether GC is "better" or not, and once you have, you'll probably realize that there's not a simple answer to that.


- Dave


alex.be...@gmail.com

unread,
Feb 12, 2020, 9:07:51 PM2/12/20
to golang-nuts
Honestly, typical Go programs don't use that many complicated concurrent primitives. Typical patterns are "work fanout" and "overseer thread", both of which have very simple lifetime rules.

That's why a lot of typical server Go code can be ported to Rust rather easily.

On Wednesday, February 12, 2020 at 1:36:40 PM UTC-8, Michael Jones wrote:
To me it seems the issue of concurrency and dynamic ownership of memory are so deeply connected to Go’s programming methodology that the “no GC” comparison is biased. 

In particular, coding to do it yourself but as perfectly as the GC across many concurrent routines is hard. Doing it better than the GC is hard. Caution encourages use of the tuned GC. 

Agree with posts above: preallocation is fastest. Hard real time from the 80s lesson. 

alex.be...@gmail.com

unread,
Feb 12, 2020, 9:22:14 PM2/12/20
to golang-nuts
Nope. G1GC actually dates back to 2004 (see doi 10.1.1.63.6386) with Metronome even earlier (2002, I think).

Zing has actually even less throughput than the good old CMS and way more memory overhead on massively-parallel systems. However, it does guarantee realtime performance that is necessary for high-speed financial apps. Shenandoah is similar.

And it's not getting better. On systems with hundreds of CPUs even small stop-the-world pauses are unacceptable, but making a pauseless compacting GC for a shared-memory system seems to be a fool's errand. By leaving out compaction, the benefits of GC become even less appealing.

Ian Lance Taylor

unread,
Feb 12, 2020, 10:04:43 PM2/12/20
to Robert Engels, alex.be...@gmail.com, golang-nuts
On Wed, Feb 12, 2020 at 2:56 PM Robert Engels <ren...@ix.netcom.com> wrote:
GCs have radically improved since then - at least in practical implementation.

Again, see G1, Metronome, Zing or Shenandoah - none of these were available in 2005.

(Or even Go's GC performance progression - but as I mentioned, in this particular test the lack of a generational collector is holding it back).

I just want to add a note that, as we've said before, it's not obvious that the generational hypothesis holds for real Go code.  See the discussion by Rick Hudson at  https://blog.golang.org/ismmkeynote.  The generational hypothesis does seem to hold for languages like Java, and for those languages generational GC is a win.  It's not obvious that it is a win for Go.  So far the experimental evidence for that claim is lacking, and it's not for lack of trying.

(Background for those who don't follow GC: the generational hypothesis is "most objects die in the nursery", or, to put it another way, most objects die quickly.  In Go, on the other hand, it appears that most objects live on the stack, and thus die without being allocated or examined by the collector.  While of course in Go some heap objects die quickly, it's not obvious that most do, and if most objects don't die quickly then generational GC is a lot of work for little gain.  To make things worse, in real Go programs programmers tend to use pools (either sync.Pool or hand rolled) for objects that die quickly, so again generational GC doesn't help.  It's possible that if Go had generational GC people would use fewer pools, but it's also true that for typical uses pools are likely to be more efficient than generational GC.)

Ian

Robert Engels

unread,
Feb 12, 2020, 11:18:34 PM2/12/20
to alex.be...@gmail.com, golang-nuts
G1GC only went into production with Java 7 in 2011.

I don’t think you understand how Zing works. Furthermore, malloc based systems actually have longer pauses especially as things get fragmented. 

I believe your knowledge of modern GC is way out of date. 

On Feb 12, 2020, at 8:22 PM, alex.be...@gmail.com wrote:


To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/36d34a8b-435d-4dab-b3b7-3d3471ff7428%40googlegroups.com.

robert engels

unread,
Feb 13, 2020, 12:59:43 AM2/13/20
to alex.be...@gmail.com, golang-nuts
Here is some pretty indisputable evidence on the advancements of GC/JVM vs C++.

See this paper/project https://github.com/hundt98847/multi-language-bench that was done by Google in 2011. If you read the paper, the C++ code was tuned by expert C++ programmers at Google. The java_pro author version refused to optimize further - which he felt would create “esoteric non-idomatic” Java.

The paper reports that the C++ code outperformed java_pro by 4x - 12x, and go_pro by 5.5x

I re-ran the tests using the latest C++ compiler,Go,  and Java 13. The code is unchanged since it was posted. Nothing has been modified (Go needed the directory structure changed to compile). Different hardware. Different OS. Different compilers. Different Java runtime. (same JVM settings - so using a different default GC). All tests run on the same OS/hardware.

C++ (-O2) = 16.5 secs
C++ (-O3) = 16.5 secs
go_pro = 19 secs
java_pro = 8.4 secs

Or Java is almost 2x faster than C++, and Go is nearly the same performance as C++.

Run the tests yourself… (easy to do, Makefiles included)

JVM/GC has improved DRAMATICALLY in the past 9 years - static compilation/optimization not so much… Stressing again - ZERO CODE CHANGES !

Enjoy !


robert engels

unread,
Feb 13, 2020, 1:13:52 AM2/13/20
to alex.be...@gmail.com, golang-nuts
Also, I installed gnu gcc 9.2 and used it rather than the OSX clang, and optimized C++ (-O2) time was 17.9 secs.

alex.be...@gmail.com

unread,
Feb 13, 2020, 2:33:06 AM2/13/20
to golang-nuts
I understand how Zing works quite well, thank you. And no, it's still horrible.

"Longer pauses in malloc" are fairy tales for children. No real allocator these days (including the slow glibc allocator) gets "fragmented".

On Wednesday, February 12, 2020 at 8:18:34 PM UTC-8, robert engels wrote:
G1GC only went into production with Java 7 in 2011.

I don’t think you understand how Zing works. Furthermore, malloc based systems actually have longer pauses especially as things get fragmented. 

I believe your knowledge of modern GC is way out of date. 

alex.be...@gmail.com

unread,
Feb 13, 2020, 2:41:08 AM2/13/20
to golang-nuts
The evidence is very disputable. For example, I don't know who was writing your paper, but they were NOT expert C++ programmers.

As an example:
for (MaoCFG::NodeMap::iterator bb_iter = CFG_->GetBasicBlocks()->begin();
bb_iter != CFG_->GetBasicBlocks()->end(); ++bb_iter) {
number[(*bb_iter).second] = kUnvisited;
}


This block is bad. The compiler will not be able to optimize it and will do an expensive indirect lookup on each iteration. It needs to be rewritten as:

for (MaoCFG::NodeMap::iterator bb_iter = CFG_->GetBasicBlocks()->begin(), bbend = CFG_->GetBasicBlocks()->end();
   bb_iter != bb_end; ++bb_iter) {
   number[(*bb_iter).second] = kUnvisited;
}

The same kind of rookie mistakes (made by fresh-from college developers?) is all over the place. Honestly, I think that they pessimized the C++ code until they reached the desired outcome.

If you want a somewhat more realistic range of benchmarks, look at Debian's Benchmark game: https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/java.html

unread,
Feb 13, 2020, 2:47:38 AM2/13/20
to golang-nuts
On Thursday, February 13, 2020 at 6:59:43 AM UTC+1, robert engels wrote:
Here is some pretty indisputable evidence on the advancements of GC/JVM vs C++.

See this paper/project https://github.com/hundt98847/multi-language-bench that was done by Google in 2011. If you read the paper, the C++ code was tuned by expert C++ programmers at Google. The java_pro author version refused to optimize further - which he felt would create “esoteric non-idomatic” Java.

The paper reports that the C++ code outperformed java_pro by 4x - 12x, and go_pro by 5.5x

I re-ran the tests using the latest C++ compiler,Go,  and Java 13. The code is unchanged since it was posted. Nothing has been modified (Go needed the directory structure changed to compile). Different hardware. Different OS. Different compilers. Different Java runtime. (same JVM settings - so using a different default GC). All tests run on the same OS/hardware.

C++ (-O2) = 16.5 secs
C++ (-O3) = 16.5 secs
go_pro = 19 secs
java_pro = 8.4 secs

Or Java is almost 2x faster than C++, and Go is nearly the same performance as C++.

Run the tests yourself… (easy to do, Makefiles included)

JVM/GC has improved DRAMATICALLY in the past 9 years - static compilation/optimization not so much… Stressing again - ZERO CODE CHANGES !

Enjoy !

One can get Java performance from the C++ version by changing 3 lines of code: std::map -> std::unordered_map. The Java versions are using hashmaps as well.

java_pro: Java 8 outperforms Java 13 by about 20%.

IPC (instructions per clock) and runtime improve when the C++ code is compiled to target x86-32 (-m32 gcc option) instead of x86-64, outperforming Java 8 in elapsed time by 10%.

The Java versions are consuming 10-20 times the memory of the C++ version.

alex.be...@gmail.com

unread,
Feb 13, 2020, 2:51:52 AM2/13/20
to golang-nuts
I actually remember another actual example of a code that does non-trivial work and is written in multiple languages: https://github.com/ixy-languages/ixy-languages

It's pretty clear that C or Rust still easily beat any GC-based language. Go and C# come close because the code for the driver utilizes pooling and value-based programming.

Brian Candler

unread,
Feb 13, 2020, 4:28:43 AM2/13/20
to golang-nuts
On Wednesday, 12 February 2020 22:57:04 UTC, robert engels wrote:
(Or even Go's GC performance progression - but as I mentioned, in this particular test the lack of a generational collector is holding it back).


This is discussed in great detail here:

TL;DR: generational GC for go has been done, but for various reasons which that presentation goes into, it turns out to be worse in some important cases than the status quo.

Nigel Tao

unread,
Feb 13, 2020, 6:42:31 AM2/13/20
to robert engels, golang-nuts
On 2/13/20, robert engels <ren...@ix.netcom.com> wrote:
> See this paper/project https://github.com/hundt98847/multi-language-bench
> <https://github.com/hundt98847/multi-language-bench> that was done by Google
> in 2011.
>
> ...
>
> Or Java is almost 2x faster than C++, and Go is nearly the same performance
> as C++.

If you're quoting that paper's numbers, or re-measuring its programs,
please also be aware of https://blog.golang.org/profiling-go-programs
which says:

"At Scala Days 2011, Robert Hundt presented a paper titled Loop
Recognition in C++/Java/Go/Scala. The paper implemented a specific
loop finding algorithm...
By using Go's profiling tools to identify and correct specific
bottlenecks, we can make the Go loop finding program run an order of
magnitude faster and use 6x less memory. (Update: Due to recent
optimizations of libstdc++ in gcc, the memory reduction is now 3.7x.)"

That blog post goes on to show a C++ program that solves the same
problem, but on Russ' hardware, is 8x faster than the original paper's
C++ program.

The 2020 JVM may very well be better than the 2011 JVM, but I don't
think this paper (even with the 2020 JVM) settles the GC (e.g. Java)
versus C++ argument.

Robert Engels

unread,
Feb 13, 2020, 9:05:45 AM2/13/20
to alex.be...@gmail.com, golang-nuts
I understand that English may not be your primary language, but please reread the first sentence again. 

Beyond that you still miss the point. 

The code hasn’t been changed. The performance numbers have changed dramatically with no developer intervention. No “hand optimizing” required. 

The fact that C++ cant optimize that loop as written only proves my other points. They do in fact use that technique in the C++ code in many places - not all. Pretty typical what you will find in the real world. 

If you want to call Robert Hundt a hack, who are you???




On Feb 13, 2020, at 1:41 AM, alex.be...@gmail.com wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/025972ea-042c-4ee9-9f11-8805e8104316%40googlegroups.com.

Robert Engels

unread,
Feb 13, 2020, 9:20:29 AM2/13/20
to alex.be...@gmail.com, golang-nuts
To reiterate, this demonstrates the advancements in GC and JVM tech. These types of tests aren’t well representative of Java’s current sweet spot - which is long running server processes with Gigs of memory, but it still perform admirably - even when including startup costs and runtime memory overhead. Go is a great alternative to C++ for these types of tools. 

I’ve used multiple “standard C++ libraries” in the financial world. In almost all cases a modern java version outperforms them because no one has the guts to go back and hand optimize due to fear of braking things - so they stagnate. 

Rust may not suffer the same fate due to its memory safety, but IMO the structure is still going to be a barrier to large program optimizations. 


On Feb 13, 2020, at 8:05 AM, Robert Engels <ren...@ix.netcom.com> wrote:



Robert Engels

unread,
Feb 13, 2020, 9:33:21 AM2/13/20
to alex.be...@gmail.com, golang-nuts
One other minor point on the proposed optimization, if this were a concurrent collection you wouldn’t do this (eg a ConcurrentHashMap in Java) because the end point changes. 

So again, more cognitive load and maintenance issues in the C++ world as you need far deeper knowledge of the systems (interface based design is not fully possible if you want performance too). 

On Feb 13, 2020, at 8:05 AM, Robert Engels <ren...@ix.netcom.com> wrote:



unread,
Feb 13, 2020, 10:57:49 AM2/13/20
to golang-nuts
On Thursday, February 13, 2020 at 3:05:45 PM UTC+1, Robert Engels wrote:
The code hasn’t been changed. The performance numbers have changed dramatically with no developer intervention. No “hand optimizing” required.

C++ evolves over time also. Hashmaps have been added to C++ in C++11, which from today's viewpoint invalidates most benchmarks published before year 2011 that translated Java's HashMap to C++'s std::map.

Robert Engels

unread,
Feb 13, 2020, 12:33:55 PM2/13/20
to ⚛, golang-nuts
I won't dispute that, but at least this particular case, it requires on-going maintenance by the developer (company). In this case of Java very few performance improvements have required code changes.

Using a different structure entirely (hash map vs. tree map) is a different issue - it touches on the robustness of the stdlib, dynamic runtime class replacement, etc. I can only assume that std::map was used because it made the code easier/portable to write in C++ rather than adding an outside dependency or custom hash map. implementation (In the Java code, they use a custom IntegerSet, IntegerList to avoid boxing...)

-----Original Message-----
From: ⚛ <0xe2.0x...@gmail.com>
Sent: Feb 13, 2020 9:57 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

On Thursday, February 13, 2020 at 3:05:45 PM UTC+1, Robert Engels wrote:
The code hasn’t been changed. The performance numbers have changed dramatically with no developer intervention. No “hand optimizing” required.

C++ evolves over time also. Hashmaps have been added to C++ in C++11, which from today's viewpoint invalidates most benchmarks published before year 2011 that translated Java's HashMap to C++'s std::map.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Robert Engels

unread,
Feb 13, 2020, 1:02:37 PM2/13/20
to Robert Engels, ⚛, golang-nuts
@atom ... I changed the cpp code to use unordered_map (cc and h) and it made the code slower... (iteration over most hash map implementations is slower than sorted maps since often you need to skip the unused key slots).

So I would be very curious to see your code changes where this brought the performance to the level of Java ?

This is not my experience. Did you actually do this?


-----Original Message-----
From: Robert Engels
Sent: Feb 13, 2020 11:33 AM
To: ⚛ <0xe2.0x...@gmail.com>, golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

I won't dispute that, but at least this particular case, it requires on-going maintenance by the developer (company). In this case of Java very few performance improvements have required code changes.

Using a different structure entirely (hash map vs. tree map) is a different issue - it touches on the robustness of the stdlib, dynamic runtime class replacement, etc. I can only assume that std::map was used because it made the code easier/portable to write in C++ rather than adding an outside dependency or custom hash map. implementation (In the Java code, they use a custom IntegerSet, IntegerList to avoid boxing...)

-----Original Message-----
From: ⚛ <0xe2.0x...@gmail.com>
Sent: Feb 13, 2020 9:57 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

On Thursday, February 13, 2020 at 3:05:45 PM UTC+1, Robert Engels wrote:
The code hasn’t been changed. The performance numbers have changed dramatically with no developer intervention. No “hand optimizing” required.

C++ evolves over time also. Hashmaps have been added to C++ in C++11, which from today's viewpoint invalidates most benchmarks published before year 2011 that translated Java's HashMap to C++'s std::map.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/f5a4a3cf-e4ce-4fb4-af03-fa5f32a5ce62%40googlegroups.com.



--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

unread,
Feb 13, 2020, 1:17:11 PM2/13/20
to golang-nuts
Just a thought: It would be an interesting experiment to count the memory usage in the C++ version of the benchmark to match Java's memory consumption (1.5-2.5 GB) and to deallocate only those C++ objects which increase memory consumption beyond this limit. At the end of the benchmark, C++ could just leave the 1.5-2.5 GB of object-bytes on the heap, without ever deallocating them in C++, and return from the main() function to take advantage of the fact that the memory will be deallocated by the operating system when the benchmark process terminates.

In a radical version of this idea, all calls to free() and delete that do not have side-effects and do not cause data cache trashing could in any C++ application be no-ops (empty operations) until memory consumption reaches 1 GB. Memory allocation until 1 GB is reached is of course just an ADD instruction, because that memory will never be reused for other objects.

Maybe this strategy is a part of how Java is able to outperform C++.

On Thursday, February 13, 2020 at 6:33:55 PM UTC+1, Robert Engels wrote:
I won't dispute that, but at least this particular case, it requires on-going maintenance by the developer (company). In this case of Java very few performance improvements have required code changes.

Using a different structure entirely (hash map vs. tree map) is a different issue - it touches on the robustness of the stdlib, dynamic runtime class replacement, etc. I can only assume that std::map was used because it made the code easier/portable to write in C++ rather than adding an outside dependency or custom hash map. implementation (In the Java code, they use a custom IntegerSet, IntegerList to avoid boxing...)

-----Original Message-----
From: ⚛ <0xe2.0...@gmail.com>
Sent: Feb 13, 2020 9:57 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

On Thursday, February 13, 2020 at 3:05:45 PM UTC+1, Robert Engels wrote:
The code hasn’t been changed. The performance numbers have changed dramatically with no developer intervention. No “hand optimizing” required.

C++ evolves over time also. Hashmaps have been added to C++ in C++11, which from today's viewpoint invalidates most benchmarks published before year 2011 that translated Java's HashMap to C++'s std::map.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

Alex Besogonov

unread,
Feb 13, 2020, 1:19:19 PM2/13/20
to Robert Engels, golang-nuts
No. It doesn’t prove anything. The C++ code is badly written, as it creates completely superfluous objects (probably optimized away) and does an indirect lookup (which will NOT be). This needs to be fixed even if the code is a faithful translation of Java code. 

So in practice all the supposed benchmarks showing GC advantage are basically flawed.

Think about it, the newest popular languages: Swift, Rust and now Kotlin Native, - all have eschewed GC because it’s simply unworkable.

Robert Engels

unread,
Feb 13, 2020, 1:46:23 PM2/13/20
to Alex Besogonov, golang-nuts
The C++ code is the original production code. The other languages were created from the C++ version.

Swift uses GC - it uses a reference counting GC which has been proven to be inferior compared to tracing collectors - especially in concurrent environments. Not to mention cycles.

Rust has multiple 'reference counting' based GC - and Rust ownership is essentially references counting of 1 with exclusive ownership.

Kotlin native uses GC as well - reference counting.

I suggest you tone it down a bit, and some more research would be helpful.

-----Original Message-----
From: Alex Besogonov
Sent: Feb 13, 2020 12:18 PM
To: Robert Engels
Cc: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

Alex Besogonov

unread,
Feb 13, 2020, 1:49:13 PM2/13/20
to Robert Engels, golang-nuts
Then other languages should faithfully replicate C++ code. For example, iteration in Java should be done like this:

for( AtomicReference<Iterator> iter = arr.iterate(); iter.get().hasNext(); iter.get().next()) {
...
}

And no, reference counting is NOT a GC.

Robert Engels

unread,
Feb 13, 2020, 2:12:46 PM2/13/20
to Alex Besogonov, golang-nuts
Reference counting is GC (at least if you are using automatic reference counting, which is what all of cited systems do). It was one of the earliest forms of GC as it is the easiest to implement.

That Java code does not represent the C++ code. The C++ code is in no way concurrent. The suggested Java code creates all sorts of "happens before" semantics that are not in any of the language versions.

Clearly my advice will not be heeded as all of your points are tinted versions of the same incorrect premises.

unread,
Feb 13, 2020, 2:23:35 PM2/13/20
to golang-nuts
On Thursday, February 13, 2020 at 7:02:37 PM UTC+1, Robert Engels wrote:
@atom ... I changed the cpp code to use unordered_map (cc and h) and it made the code slower... (iteration over most hash map implementations is slower than sorted maps since often you need to skip the unused key slots).

It is more likely that if hash-map iteration is slower than sorted-map it is because of an increase in LLC (last-level cache) misses.
 
So I would be very curious to see your code changes where this brought the performance to the level of Java ?

It depends on the CPU used to run the benchmarks. In particular, a large L3 cache seems to help a lot.

On a notebook CPU with 2 MB of LLC, x86-64 mode, the performance with std::unordered_map (849e6 LLC misses) indeed isn't better than std::map (605e6 misses).

However, on a desktop CPU with 32 MB of LLC, x86-64 mode, std::unordered_map has (if I am reading it correctly) 2.0e6 misses and std::map has 2.2e6 misses.

Changing the compiler target to x86-32 (gcc switch: -m32) improves performance on both CPUs.

The complete git-diff in directory multi-language-bench/src/havlak/cpp, except #include directives, is:

-  typedef std::map<BasicBlock*, int> BasicBlockMap;
+  typedef std::unordered_map<BasicBlock*, int> BasicBlockMap;

-  typedef std::map<int, BasicBlock*> NodeMap;
+  typedef std::unordered_map<int, BasicBlock*> NodeMap;
 

This is not my experience. Did you actually do this?

It is probable some CPU performance counter can explain the difference in our experiences.
 


-----Original Message-----
From: Robert Engels
Sent: Feb 13, 2020 11:33 AM
To: ⚛ <0xe2.0...@gmail.com>, golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

I won't dispute that, but at least this particular case, it requires on-going maintenance by the developer (company). In this case of Java very few performance improvements have required code changes.

Using a different structure entirely (hash map vs. tree map) is a different issue - it touches on the robustness of the stdlib, dynamic runtime class replacement, etc. I can only assume that std::map was used because it made the code easier/portable to write in C++ rather than adding an outside dependency or custom hash map. implementation (In the Java code, they use a custom IntegerSet, IntegerList to avoid boxing...)

-----Original Message-----
From: ⚛ <0xe2.0...@gmail.com>
Sent: Feb 13, 2020 9:57 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

On Thursday, February 13, 2020 at 3:05:45 PM UTC+1, Robert Engels wrote:
The code hasn’t been changed. The performance numbers have changed dramatically with no developer intervention. No “hand optimizing” required.

C++ evolves over time also. Hashmaps have been added to C++ in C++11, which from today's viewpoint invalidates most benchmarks published before year 2011 that translated Java's HashMap to C++'s std::map.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

David Riley

unread,
Feb 13, 2020, 2:47:10 PM2/13/20
to Robert Engels, Alex Besogonov, golang-nuts
On Feb 13, 2020, at 1:45 PM, Robert Engels <ren...@ix.netcom.com> wrote:
>
> Swift uses GC - it uses a reference counting GC which has been proven to be inferior compared to tracing collectors - especially in concurrent environments. Not to mention cycles.

Worth noting, too, that Swift does this largely because it inherits its default runtime from Objective-C, which started as a manually refcounted language and added automatic refcounting GC-ish behavior later in life. Swift was designed to be a more intuitive replacement for ObjC, so it inherits most of its benefits and curses.


- Dave

Robert Engels

unread,
Feb 13, 2020, 3:27:36 PM2/13/20
to ⚛, golang-nuts
I find it difficult to believe that

"However, on a desktop CPU with 32 MB of LLC, x86-64 mode, std::unordered_map has (if I am reading it correctly) 2.0e6 misses and std::map has 2.2e6 misses."

level of cache misses is going to improve the total performance by 2x, but I can't really test. I am running all of the tests on the same hardware.

But I ran the cpp today at work, on a machine with 8MB L3 and there was no difference between map and unordered_map.




To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/b4dadd10-e44c-458c-9d27-fbd3d7e6ba93%40googlegroups.com.



Robert Engels

unread,
Feb 13, 2020, 3:50:00 PM2/13/20
to Robert Engels, ⚛, golang-nuts
For those that are still interested in this...

An interesting point - when you run the Java or Go versions you will see CPU usage in excess of 100% - an indicator that the runtimes are parallelizing the work of GC with the application - something that (I don't believe) a malloc based application do (but conceivable it could predict potential fragmentation/oom conditions points and start asking the OS for more memory in the bg).

So not only is there additional memory consumption, but there is additional CPU usage as well - you don't have to code for the performance increases - but the costs as still there.

robert engels

unread,
Feb 13, 2020, 9:32:54 PM2/13/20
to ⚛, golang-nuts
What you propose is actually a common solution in many “older” financial Java applications. They allocate a huge heap,  and turn off GC, and then exit the process daily. You can do similar machinations in a “server functions / web handler” system by pre-allocating lots of JVMs and continually killing and restarting them in the background to handle requests - essentially never paying a GC penalty (but lots of other costs). The same technique could easily be applied to C++ programs - just no-op free() (not calling the destructors is a bit more difficult).

I ran the cpp code (using unordered_map) under the profiler (sorry for the screen capture, but the text results were unusable):


You can clearly see that the vast majority of CPU time was consumed by allocation and de-allocation. Which brings me back to my original point - if you can write your system without dynamic memory in a maintainable fashion - go for it - if not, a GC environment is going to show the best performance. Allocations are essentially free, and deallocations are amortized and can be done in the background across multiple processors. You “might" increase the variability (although the bounded GC has really matured), but probably no more so than using off-the-shelf collectors in standard C/C++.

Regardless of what some have said, malloc() and free() are HIGHLY variable under most work loads. You can try to work around it with custom allocators, but you are essentially re-implementing GC by yourself.






To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/33723531-2ad2-4161-ae3e-14ec375cbb90%40googlegroups.com.

unread,
Feb 14, 2020, 9:35:07 AM2/14/20
to golang-nuts
On Friday, February 14, 2020 at 3:32:54 AM UTC+1, robert engels wrote:
You can clearly see that the vast majority of CPU time was consumed by allocation and de-allocation.

In the case of this particular benchmark, it is pointless to be pointing out the cost of individual malloc+free calls because the memory is being deallocated in a for-loop:

for(o in object-list) { delete o; }

and it is probable that most experienced C++ programmers would during optimization convert this for-loop to use delete[] or use std::vector to hold the objects.

Robert Engels

unread,
Feb 14, 2020, 9:43:40 AM2/14/20
to ⚛, golang-nuts
If each object exists independently - which it does in this case - you must use a free on each object. So you are going to loop - it just may be hidden from you. 

The idea was to avoid all free calls and terminate the process instead - which I pointed out has been a solution for quite a long time. 

On Feb 14, 2020, at 8:35 AM, ⚛ <0xE2.0x...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

unread,
Feb 14, 2020, 9:52:32 AM2/14/20
to golang-nuts
On Friday, February 14, 2020 at 3:43:40 PM UTC+1, Robert Engels wrote:
If each object exists independently - which it does in this case - you must use a free on each object. So you are going to loop - it just may be hidden from you.

I am sorry, I do not understand your style of reasoning in general. 

Jesper Louis Andersen

unread,
Feb 14, 2020, 9:53:11 AM2/14/20
to Alex Besogonov, Robert Engels, golang-nuts
On Thu, Feb 13, 2020 at 7:48 PM Alex Besogonov <alex.be...@gmail.com> wrote:
And no, reference counting is NOT a GC.


General consensus, at least among academia, is that automated reference counting is a GC scheme. You may argue that it is not based on whether it is implicitly done (Like Ruby and Python did/does), or explicitly done (as in Swift, C++, ...).

One strong argument as to why is the idea that mark'n'sweep and refcount are each others "duals". See "A Unified theory of garbage collection" by Bacon, et.al.

Robert Engels

unread,
Feb 14, 2020, 11:57:40 AM2/14/20
to ⚛, golang-nuts
If you have a container of objects, you still need to free each object. If it is an "container" of value objects (one reference to a block of memory), you can free once at the block level, but if the block needs to be expanded (dynamic memory), you need to copy all of the data (or use a complex sparse array structure, and then you pay the performance hit during iteration). There is no free lunch.

-----Original Message-----
From: ⚛ <0xe2.0x...@gmail.com>
Sent: Feb 14, 2020 8:52 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

On Friday, February 14, 2020 at 3:43:40 PM UTC+1, Robert Engels wrote:
If each object exists independently - which it does in this case - you must use a free on each object. So you are going to loop - it just may be hidden from you.

I am sorry, I do not understand your style of reasoning in general. 

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Alex Besogonov

unread,
Feb 14, 2020, 12:02:08 PM2/14/20
to Robert Engels, ⚛, golang-nuts
No, you do not. In C++ you would put object values in the vector, rather than pointers to objects.

You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/gPoYIPBGy24/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/1662044109.1005.1581699420223%40wamui-sophie.atl.sa.earthlink.net.

Robert Engels

unread,
Feb 14, 2020, 12:24:08 PM2/14/20
to Alex Besogonov, ⚛, golang-nuts

Yes, and when you store value objects in a vector and it is resized, it is very expensive as you need to make copies of "large objects", vs a "pointer to an object".

Which is better depends on the object lifetimes, size of objects, access patterns, etc.

Clearly the Googlers that wrote the code felt that using pointers rather than value objects was more appropriate. I didn't write that code, nor have I investigated the code enough to have an opinion as to whether that was an appropriate choice - but I assume they did.
-----Original Message-----
From: Alex Besogonov

Robert Engels

unread,
Feb 14, 2020, 12:34:36 PM2/14/20
to Alex Besogonov, ⚛, golang-nuts
Also, there are many other considerations - like if the objects are const, if not, using smart pointer refs may be the only way in a concurrent system, etc.

There's a lot of factors to consider in value vs. reference - I trust the Googlers investigated which was most appropriate.

-----Original Message-----
From: Robert Engels

unread,
Feb 14, 2020, 12:37:02 PM2/14/20
to golang-nuts
On Friday, February 14, 2020 at 6:24:08 PM UTC+1, Robert Engels wrote:

Yes, and when you store value objects in a vector and it is resized, it is very expensive as you need to make copies of "large objects", vs a "pointer to an object".

A paged vector neither copies nor moves the objects when it is resized.

Direct access to memory offers some optimization opportunities that are not directly possible in a virtual machine programming language.

Robert Engels

unread,
Feb 14, 2020, 12:46:51 PM2/14/20
to ⚛, golang-nuts
Yes, and then the access and iteration is slower as it needs indirection to find the correct page. There is no free lunch.

The caveats about using mutable objects, sharing, and concurrency still apply.

A virtual machine environment has nothing to do with preventing direct memory access. You can do direct memory access in Java. I think what you are referring to is a "managed memory", or "safe memory" environment.

Honestly, this stuff is CS 101 (maybe 201), and we've strayed so far off the topic. I didn't write the code. Take it up with those Googlers if you think it's bad. I was using the code as a baseline to demonstrate improvements in JVM/GC technology, nothing more - and for that it was appropriate.

-----Original Message-----
From: ⚛ <0xe2.0x...@gmail.com>
Sent: Feb 14, 2020 11:37 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

unread,
Feb 14, 2020, 12:57:08 PM2/14/20
to golang-nuts
On Friday, February 14, 2020 at 6:46:51 PM UTC+1, Robert Engels wrote:
Yes, and then the access and iteration is slower as it needs indirection to find the correct page. There is no free lunch.

The caveats about using mutable objects, sharing, and concurrency still apply.

A virtual machine environment has nothing to do with preventing direct memory access. You can do direct memory access in Java. I think what you are referring to is a "managed memory", or "safe memory" environment.

Honestly, this stuff is CS 101 (maybe 201), and we've strayed so far off the topic. I didn't write the code. Take it up with those Googlers if you think it's bad. I was using the code as a baseline to demonstrate improvements in JVM/GC technology, nothing more - and for that it was appropriate.

You seem to believe that no further improvements in C++ compiler technology are possible. Within the next 100 years mankind is surely going to develop a C++ compiler which will automatically replace std::map with std::unordered_map in this particular benchmark, will automatically redirect relevant new/delete expressions to a fast memory pool allocator and will automatically use 24/32-bit addresses instead of 64-bit ones if it makes sense from performance viewpoint.

Of course, JVM/GC technologies are going to improve over time as well. The belief that C++ will not is false.

Robert Engels

unread,
Feb 14, 2020, 1:14:05 PM2/14/20
to ⚛, golang-nuts
I'll be long dead - and also never made any claims that it wasn't possible - only that it hasn't happened.

I will wager though that if there is such a thing a programming in 100 years, "developers" won't be doing manual memory management.

-----Original Message-----
From: ⚛ <0xe2.0x...@gmail.com>
Sent: Feb 14, 2020 11:57 AM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

unread,
Mar 3, 2020, 4:54:54 PM3/3/20
to golang-nuts
A link related to a potential step in the evolution C++: http://wg21.link/p1609

Robert Engels

unread,
Mar 3, 2020, 5:25:22 PM3/3/20
to ⚛, golang-nuts
A key statement in the link “ The JIT-generated code is significantly faster than the ahead-of-time-generated code for small matrix sizes.”

Which is what you were arguing was not possible... you can’t have it both ways. 

On Mar 3, 2020, at 3:55 PM, ⚛ <0xE2.0x...@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

unread,
Mar 3, 2020, 5:35:03 PM3/3/20
to golang-nuts
On Tuesday, March 3, 2020 at 11:25:22 PM UTC+1, Robert Engels wrote:
A key statement in the link “ The JIT-generated code is significantly faster than the ahead-of-time-generated code for small matrix sizes.”

Which is what you were arguing was not possible... you can’t have it both ways.

There is another piece of information that can be found there: runtime performance of Single Specialization is approximately the same as runtime performance of the JIT-ed code.

Robert Engels

unread,
Mar 3, 2020, 5:53:19 PM3/3/20
to ⚛, golang-nuts
I think having a single specialization is nearly impossible in a real-world application for a structure like a matrix so I am not really sure of the value. 

It's kind of strange that they even call that a specialization - my (limited) understanding of specializations is when you write custom implementations for certain parameters types (the classic sort() uses counting sort for small domains (char keys), and quick-sort for others (arbitrary keys)).

-----Original Message-----
From: ⚛ <0xe2.0x...@gmail.com>
Sent: Mar 3, 2020 4:35 PM
To: golang-nuts
Subject: Re: [go-nuts] Go without garbage collector

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Kevin Chadwick

unread,
Mar 4, 2020, 6:08:20 AM3/4/20
to golang-nuts
On 2020-03-03 22:22, Robert Engels wrote:
> A key statement in the link “ The JIT-generated code is *significantly* faster
> than the ahead-of-time-generated code for small matrix sizes.”
>
> Which is what you were arguing was not possible... you can’t have it both ways.

JIT code often requires RWX memory and JAVA has long been a violator of security
protections in this area. I believe Pythons crypto package is still a RWX memory
creator.

I hope the strive for performance doesn't come at the cost of reducing the
potential of an OS or memory protection unit/management to protect us. Coupled
with restarting failing programs, it is a potentially dangerous default practice.

IMO, performance desires by a smaller group have kept C insecure and hurt the
masses!

"https://phoronix.com/scan.php?page=news_item&px=OpenBSD-Browser-Security"
"https://marc.info/?l=openbsd-misc&m=142523501726732"
Reply all
Reply to author
Forward
0 new messages