> So it seems like you'd expect to see almost no difference except in the most 
> extreme cases, and even in those cases the Nim program would likely just be 
> producing unnecessary garbage anyway, right?

Not entirely what I meant, though I may not have been clear. Some imperative 
algorithms are allocation-heavy even if you write them naturally and if they 
occur in a hot loop, you can have a problem.

However, there are a few points to make:

1\. You can generally make them more efficient using imperative code. The 
standard insertion algorithm for red-black-trees requires O(1) allocation work 
per insertion (and also O(1) memory writes overall, but of course still O(log 
n) memory reads); a purely functional red-black-tree implementation requires 
O(log n) allocation work per insertion.

2\. You can reduce the allocation work in many cases. There are a number of 
common approaches:

(a) Represent e.g. a tree as an array and references as integer offsets within 
the array.

(b) If your allocator is slow, keep a size-limited free list per data 
structure. Say, up to 100 entries. Whenever you free a node, put it on the free 
list, unless it exceeds it max size, then you free it normally. To allocate a 
node, you go to the free list first, unless it's empty, in which case you 
allocate normally. This means for data structures that fluctuate in size, you 
save a fair amount of allocations.

(c) Represent subjects as tuples that reference another data structure. For 
example, I had a string interning implementation I wrote once, where I didn't 
allocate the string that was to be interned separately (which in most cases 
would have been discarded right after the interning operation), but passed a 
(original string, start, length) triple. String interning allocated a string 
only if there wasn't already an interned variant.

Note that on contemporary hardware with state of the art GC, there's a limit on 
how much you can allocate. This is difficult to reach within a single thread, 
but if you use multiple threads and use a purely functional style, [you can run 
fairly easily into 
it](https://gist.github.com/plokhotnyuk/42af1ebfeac791bb5b52bbaeb974acbe). Even 
the best GC can't fix that.

The core issue that I tend to get back to is that as I wrote above, most 
programs that run into GC issues have a garbage problem rather than a garbage 
collection problem. And unlike with most purely functional languages, 
imperative or multi-paradigm languages can usually avoid it or work around it.

Reply via email to