
Go channels, goroutines and GC available in Nim - pwernersbach
http://forum.nim-lang.org/t/1278
======
stcredzero
_This document describes how the GC works and how to tune it for (soft)
realtime systems._

 _The basic algorithm is Deferred Reference Counting with cycle detection._

I'm sitting here feeling very impressed. Deferred reference counting has very
good semantics for games. Even better, you can control the cycle detection
part separately and run that part at an advantageous time. (Though it's
probably better to just let GC do its thing, unless you really know what
you're doing.)

I am currently writing a multiplayer game server in golang, by making sure
almost everything is allocated on the stack, and heap sizes are small. This
gives me an efficient, nearly pauseless server. However, something like Nim
could give me even more flexibility.

~~~
pcwalton
> I'm sitting here feeling very impressed. Deferred reference counting has
> very good semantics for games.

Not if you need to be thread-safe.

> Even better, you can control the cycle detection part separately and run
> that part at an advantageous time.

How would that work with multithreading? (Assuming you had a thread-safe GC,
which Nim's isn't.)

~~~
fjehcjriendje
So, I've noticed that over the past six months that every time something about
Nim gets posted to HN, you make an effort to discredit the language.

Care to offer an explanation why?

~~~
pcwalton
I think Nim is an impressive language that does a lot of things really well. I
don't think the memory management is one of them. I believe that memory
management and compilation to C are the only two major things I've ever talked
about in regards to Nim, because I'm abstractly interested in those topics. If
an article about thread-local deferred reference counting in Ruby hit the top
of HN and the comments were talking about how that's good for games, I'd
probably comment there too.

~~~
copx
And how do you do "memory management well"? Like Rust? You pay a high price in
complexity and inflexibility for that juicy GC-less yet safe memory
management.

Ref counting is not superior or inferior to explicit, restrictive ownership
semantics. Those are simply different trade-offs.

Nim might be strictly inferior for writing a heavily multi-threaded web
browser because of its memory management approach but that does not mean the
approach is generally inferior.

Seems to me that Nim aims to be a "casual", rapid development / friendly
(Python-like) language. Ownership semantics like in Rust do not fit there.

~~~
pcwalton
I'm personally a fan of regular old Java/C#-like concurrent garbage collection
for most "scripting" languages (perhaps surprisingly, given my work on Rust).
It's a lot of work to get there, but I think there's no substitute for doing
the work—apps really end up needing the flexibility of shared memory.
Shortcuts that seem simple like DRC end up tending to run into walls in
practice, which is something that the other languages discovered—history keeps
pointing to the HotSpot-style garbage collector as the one that systems that
need to offer a simple, general-purpose garbage-collected programming model
migrate to.

For different use cases Rust-style ownership semantics (when the performance
of GC and runtime interoperability become an issue), or Azul-style pauseless
GC (when you're willing to trade throughput for latency), or shared-nothing
architectures (when you need them for legacy reasons like JavaScript, or want
a simple programming model like Erlang) can work great.

~~~
tatterdemalion
"apps really end up needing the flexibility of shared memory"

Why? Just performance or is there a design reason also?

------
kodablah
I always wanted to see a Go/Nim interop project in a little different vein.
Since Nim is a superset of Go (and Go is fairly simple), why not a Go
implementation in Nim via cross-compilation? Then you can even build a Nim
macro that does a "Go get" and Nim developers can use Golang libraries right
in their code as imports. Not to mention we get a full, alternative Go impl.

It should be easy to bootstrap by first using Go's parser to dump some AST
that Nim code can then translate, and then once that version is done, just
reference the Go parser using this new impl. The only real struggle with the
project from what I can see is deciding which standard library items to use
the Go transpiled implementation (probably most of them) vs which need to have
Nim backends (e.g. runtime package). Meh, just rambling thoughts in my head I
was considering playing with given the time...

~~~
stefantalpalaru
> why not a Go implementation in Nim via cross-compilation?

Nim's macros have one limitation that prevents an accurate implementation of
another syntax: they can't fully modify the existing syntax.

See how I had to use "scase" inside "select" blocks because the existing
"case" keyword insists on having "of" after it. So Nim's semantics put some
limits on the amount of hijacking one can inflict on it through macros.

~~~
stcredzero
If you front compilation with Go's parser, you could generate bog-standard Nim
code from the AST.

~~~
stefantalpalaru
You can, but you'd be limiting yourself to pure Go. I'd rather have Go with
Nim's generics, transpilation to C, macros, compile time computation, etc.

------
Manishearth
This is awesome! Go's channels and green threads were one of the features I
really liked about Go. One more reason for me to go past basic prime number
programs in Nim :)

I'm hoping to see Rust get green threads/tasks/goroutines too. I'm working on
a GC myself, and hopefully someone is trying out a green thread scheduler.

------
bmurphy1976
I'm very curious to hear the pros and cons of this from somebody who has
intimate knowledge of Nim internals. I really really like Go's CSP model and
would love to see it properly supported in another lightweight non-jvm
language (yes I know about Erlang, it doesn't fit the bill for me).

~~~
vbit
Well you can already use Nim threads ([http://nim-
lang.org/docs/threads.html](http://nim-lang.org/docs/threads.html)) and
channels ([http://nim-lang.org/docs/channels.html](http://nim-
lang.org/docs/channels.html)) - the model is similar although the
implementation uses system threads rather than coroutines.

Nim also has lightweight coroutines using `async` and `await` ([http://nim-
lang.org/docs/asyncdispatch.html](http://nim-
lang.org/docs/asyncdispatch.html)) - you can run a bunch of these within one
thread.

Also, have a look at gevent for Python.

~~~
rspeer
Don't look at gevent for Python, look at a responsible async library: asyncio
if you like Py3, Twisted if you like Py2 and want something that's very
featureful but old-school, Trollius if you wish you could be using asyncio but
you're stuck on Py2.

gevent is too much magic. It aims to give you async without changing any of
your code. To accomplish this, it monkey-patches the entire Python standard
library, in a way that is 99% compatible with Python, but the 1% will
constantly surprise and infuriate you. Its compatibility shows no signs of
increasing given how much its development has slowed down.

You can use gevent as a quick hack, but you will hate yourself if you have to
maintain gevent code.

~~~
rudolf0
You make a valid point, but many years of work have gone into the
monkeypatching at this point. If you use the latest version, it's very rare to
find a place where it fails. It's usually only an issue if you're dealing with
a third party module that makes use of native shared libraries.

I use gevent both for small and large projects, and haven't had any
complaints. The monkeypatching pains my soul just a little bit, but I've found
no better async framework for Python yet.

------
jeremyjh
I thought the Go runtime runs foreign code on M:M threads; e.g. when Go time
calls foreign code, it dedicates a thread to it. This is so foreign libraries
(which are unaware of yielding to the Go scheduler when they do I/O) don't
block a thread with multiple goroutines scheduled on it. I do not think Nim
code can run "in a goroutine".

~~~
stefantalpalaru
> I do not think Nim code can run "in a goroutine".

It does. Don't forget that this is gccgo so it is possible to use plain C
functions as goroutines. Nim is translated to C and with the help of a macro I
convert Nim functions with an arbitrary number of arguments into ones with a
single void* arg that gccgo wants for its 'go' keyword implementation:

    
    
        extern void* __go_go(void (*f)(void *), void *);

~~~
jeremyjh
Yes, but when the Go runtime calls that foreign function pointer, it is going
to schedule an entire OS thread for its duration isn't it? If it doesn't, then
nothing prevents foreign code from blocking other goroutines. How does the Nim
code yield the thread for other goroutines, does it have to register a
callback?

~~~
stefantalpalaru
> when the Go runtime calls that foreign function pointer, it is going to
> schedule an entire OS thread for its duration isn't it?

No.

> How does the Nim code yield the thread for other goroutines, does it have to
> register a callback?

There are no callbacks. Yielding happens automatically when launching another
goroutine, when sending, receiving or selecting on a channel. You can also
yield explicitly with go_yield() - the better named equivalent of Go's
runtime.Gosched().

It's easier to understand if you realize that all those operations with
goroutines and channels end up being done in the Go runtime.

~~~
jeremyjh
> > when the Go runtime calls that foreign function pointer, it is going to
> schedule an entire OS thread for its duration isn't it?

>No.

Are you sure? Once a thread enters cgo it is considered blocked and is removed
from the thread pool according to these sources [1][2]. I previously found a
thread where Ian Lance Taylor explained it more explicitly but I couldn't find
that now. Is that not what is happening though when __go_go invokes your
function pointer?

I do not understand how the Nim code can live in the segmented stack of a
goroutine, nor how the Go runtime could know it is time to grow that stack.

[1] [https://groups.google.com/forum/#!searchin/golang-
nuts/gorou...](https://groups.google.com/forum/#!searchin/golang-
nuts/goroutine$20OS$20thread$20C/golang-nuts/RTtMsgZi88Q/To5F9wWnpU8J)

[2][http://stackoverflow.com/questions/27600587/why-my-
program-o...](http://stackoverflow.com/questions/27600587/why-my-program-of-
golang-create-so-many-threads)

~~~
stefantalpalaru
> Are you sure?

Yes. See the chinese whispers benchmark with 500000 goroutines and a maximum
resident set size of 5.4 GB on amd64. It has the same memory usage (and run
time) as the Go version compiled with gccgo.

> Once a thread enters cgo

This has nothing to do with cgo. It's a different mechanism specific to gccgo.

> I do not understand how the Nim code can live in the segmented stack of a
> goroutine

Good thing you asked. I just ported to Nim the peano.go benchmark described as
a "torture test for segmented stacks" and... it failed. The fix was to pass
-fsplit-stack to gcc when it compiles the C code generated by nim.

> nor how the Go runtime could know it is time to grow that stack

From what I can tell it's done in __splitstack_makecontext() and friends from
GCC's libgo.

~~~
jeremyjh
Well, cool! Great work :)

