[HN Gopher] A cost model for Nim
       ___________________________________________________________________
        
       A cost model for Nim
        
       Author : WithinReason
       Score  : 170 points
       Date   : 2022-11-11 08:53 UTC (14 hours ago)
        
 (HTM) web link (nim-lang.org)
 (TXT) w3m dump (nim-lang.org)
        
       | pietroppeter wrote:
       | unrelated to the post but if you are curious on how a HN spike
       | looks like you can follow it in real time here:
       | https://plausible.io/nim-lang.org?period=day&date=2022-11-11
       | 
       | nim recently implemented public analytics for its website and I
       | think it is a fun thing and something I have not seen around
       | much: https://nim-lang.org/blog/2022/11/01/this-month-with-
       | nim.htm...
       | 
       | do you know of other open source projects which have public
       | analytics?
        
         | gw98 wrote:
         | Had no idea plausible.io existed. Thanks for posting the link
         | for this. Just what I need for a project :)
        
           | pietroppeter wrote:
           | oh plausible works great! it is paid but open source and you
           | can host it. very nice guys that run it. there are also other
           | nice options out there, but this is the only one I know where
           | it is very easy and straightforward to make your analytics
           | public!
        
       | elcritch wrote:
       | Nice to see this here! I helped edit parts of this, feel free to
       | ask me questions. Personally I've quite enjoyed using Nim in RTOS
       | and embedded systems. I believe it opens up the _full power_ of
       | embedded programming to non-experts.
       | 
       | It's hard to describe how nice the combination of ARC/ORC memory
       | management with the TLSF allocator is for a ready-to-go real time
       | code and systems programming.
       | 
       | This combo gets 90-95% the speed and size of manual memory
       | management, while being much easier to program. Generally
       | performance and real time metrics or latency is a matter of
       | optimizing your code, not re-writing it. Since Nim v2 integrates
       | the allocator it also makes it trivial to debug allocations, or
       | check for leaks.
        
       | goodpoint wrote:
       | Nice post.
        
       | gavinray wrote:
       | Not super related to the entirety of this blogpost but this bit
       | stuck out to me:                 > "If your domain is not "hard"
       | real-time but "soft" real-time on a conventional OS, you can
       | "pin" a thread to particular core via system.pinToCpu"
       | 
       | This is probably one of the easiest/nicest API's I've seen for
       | doing this in a language.
        
         | pietroppeter wrote:
         | unfortunately this pinToCpu seems to be discouraged or
         | ineffective (not for nim related reason) :/ see this
         | discussion: https://forum.nim-lang.org/t/9596#63093
        
           | tjoff wrote:
           | Not sure why it would be discouraged because of big little.
           | Pinning to a big or pinning to a little is still a
           | potentially useful thing.
        
             | cb321 wrote:
             | >Not sure why it would be discouraged
             | 
             | pinToCPU (at least in Nim) just sets scheduling affinity,
             | but does not hard-allocate like the Linux isolcpus boot
             | parameter.."pin" makes it sound more powerful than it is.
             | It is more like POSIX `nice` than POSIX `mlock`.
             | 
             | But also, `nice` exists for a reason. Your agenda might be
             | to do something as much as possible on a Little core which
             | is indeed potentially useful. pinToCPU takes a cpu number
             | and you can usually know from that number whether it is Big
             | or Little.
             | 
             | The real concern may be that the OS has a more global
             | picture of system activity than any one program..and so
             | discourage "second guessing" the scheduler. OTOH, the OS
             | probably does _not_ have a picture of how important timely
             | completion of any particular thing is - honestly that could
             | almost be end-user-provided data (much like there is a
             | `nice` command).
             | 
             | A lot of the time people motivate affinity just as a "use
             | the same L1/L2 cache" for "absolute max perf", and in that
             | sense you may be getting better perf, but over fewer cores
             | (only the big ones) or you may not do the ahead of time
             | work to map out big-little CPU numbers.
             | 
             | Scheduling is also very OS-specific. So, reasoning about
             | what Y you get when you ask for X can also be tricky in any
             | OS-portable setting.
             | 
             | Sometimes things are "so complicated" that people "who
             | know" just discourage those who do not. My inclination is
             | instead to explain what it can/cannot/does/does not do
             | better rather than discourage its use.
        
       | alberth wrote:
       | I've always been surprised Nim hasn't taken off more.
       | 
       | All the productivity of a language like Python but with the
       | performance of C.
        
         | 0cf8612b2e1e wrote:
         | No 800 pound gorilla has taken the language under its wing, so
         | really difficult to build mindshare. Without a Google,
         | Microsoft, etc who can throw unlimited funding to have full
         | time developers polishing rough edges, writing docs, or
         | implementing libraries, a new language is always lacking vs
         | established alternatives.
        
           | alberth wrote:
           | Who's the 800 lb gorilla for Rust (because it seems to have
           | large community behind it)?
        
             | 0cf8612b2e1e wrote:
             | Mozilla funded multiple full time developers for years.
             | While not a "big" company, it does have significant
             | financial resources and creates a very impactful product.
        
             | fleventynine wrote:
             | Almost every large tech company that uses C++ heavily is
             | jumping onto the Rust train.
        
           | olah_1 wrote:
           | The only thing close is some ethereum tools like these:
           | 
           | - https://github.com/status-im/nwaku
           | 
           | - https://github.com/status-im/nimbus-eth2
           | 
           | But I think the real killer feature to me is the javascript
           | target. It seems like a real dream for a fullstack
           | experience.
        
             | arc776 wrote:
             | > the real killer feature to me is the javascript target
             | 
             | Agree, this is amazing because you can share code and data
             | structures between front and backend (for example:
             | https://github.com/karaxnim/karax).
             | 
             | Also, it's really nice having high level stuff like
             | metaprogramming and static typing spanning both targets.
             | Things like reading a spec file and generating statically
             | checked APIs for server/client is straightforward, which
             | opens up a lot of possibilities.
        
               | dom96 wrote:
               | Honestly as someone that has used Nim for this
               | extensively, I think that these days with WASM and
               | TypeScript available this isn't much of a killer feature
               | anymore.
        
               | arc776 wrote:
               | Only if you consider the web side.
               | 
               | Backend and frontend in the same language is a massive
               | feature for many reasons, not least performance and
               | uniformity. Adding TypeScript is increasing interface
               | friction and complexity, instead of simplifying things.
               | 
               | Of course, right now this may or may not be
               | easier/harder/feasible with just Nim depending on the
               | project, and many more people know TypeScript, but the
               | ideal of one language > many languages is a worthy one.
        
               | dom96 wrote:
               | What do you mean? I am considering it from both sides.
               | 
               | TypeScript already allows this, i.e. you can build the
               | backend and frontend in one language (TypeScript). Same
               | for most languages that compile to WASM.
               | 
               | My point is that with TypeScript this killer feature is
               | already realised and in a way that is far more familiar
               | to the many thousands of developers that already know
               | JavaScript.
        
         | nerdponx wrote:
         | I have really enjoyed using it for random scripts and small
         | programs, but it definitely is slower than plain C for simple
         | things like "loop over lines of stdin and do some basic
         | calculations". Of course, you also get a lot of nice
         | abstraction, memory safety, GC, a great standard library,
         | compiler-checked effects (!), less-noisy syntax, and a nice
         | easy compiler CLI, and that's a tradeoff I'm happy to make. Not
         | to mention that I would feel much more comfortable writing a
         | bigger application in Nim than in C.
        
           | monetus wrote:
           | That slowness is tied to the GC, which you can swap in and
           | out with others or turn off. I feel like this is a really
           | undersold feature of the language.
        
             | cb321 wrote:
             | It's a mixture of allocation, copying, and reclamation. You
             | can actually side-step a lot of such issues in a mostly mm-
             | agnostic way with a `var` like interface in Nim -- sticking
             | to one initial allocation like the common pattern of
             | caller-allocated storage in C. These days on three-level
             | cache server CPUs the real cost of hitting virgin, uncached
             | memory is probably worse than other pieces..DRAM latency is
             | >10x L3, but almost everything "depends on..".
        
               | arc776 wrote:
               | 100% my experience too. GC in Nim is a rounding error at
               | worst and is often statically elided, anyway. Performance
               | is almost entirely about memory access patterns and you
               | have the same access to this as C.
               | 
               | In other words, performance in Nim is entirely down to
               | the algorithm used, which is a nice place to be.
        
           | cb321 wrote:
           | I have not found this slower-than-C to be the case. You may
           | need to use something like `iterator getDelims` in https://gi
           | thub.com/c-blake/cligen/blob/master/cligen/osUt.ni... to
           | manage memory with fewer copies/less alloc "more like C",
           | though or perhaps `std/memfiles` memory mapped IO.
           | 
           | More pithy ways to put it are that "there are no speed
           | limits" or "Nim responds to optimization effort about as well
           | as other low level languages like C". You can deploy SIMD
           | intrinsics, for example. In my experience, it's not that hard
           | to "pay only for what you use".
           | 
           | As a more concrete thing, I have timed (yes, on one CPU, one
           | test case, etc..many caveats) the tokenizer used in
           | https://github.com/c-blake/bu/blob/main/rp.nim to be faster
           | than that used by the Rust xsv.
           | 
           | Of course, you really shouldn't tokenize a lot if it's
           | costly/data is big, but rather save a binary answer that does
           | not need parsing (or perhaps more accurately is natively
           | parsed by the CPU).
        
             | nerdponx wrote:
             | This stuff honestly goes over my head and beyond my current
             | skillset, but it's useful to know that there is no "speed
             | limit" in the hands of someone who knows what they're
             | doing.
             | 
             | My experience has been with basically translating Python
             | and shell scripts into Nim, and occasionally comparing them
             | with my own feeble attempts at doing the same in C.
        
         | winrid wrote:
         | The reason is tooling. The Clion plugin doesn't even do type
         | checking and is a year out of date. The language server has
         | been in progress for five years and is still not done. The
         | VSCode plugin is okay, but refactoring, auto-imports, and
         | autocomplete are nowhere near as easy as other languages like
         | Java in jetbrains products. Not to mention a good debugger.
         | 
         | I hope Jetbrains can prioritize the Clion plugin. It would help
         | a lot.
        
           | fuckstick wrote:
           | Without disagreeing on the specific points about the tooling,
           | I'm not convinced. Golang took off with a far weaker tooling
           | story. Maybe the table stakes have changed in a decade - but
           | I believe other factors are more important. Moreover I remain
           | skeptical that even if those tooling issues were fixed
           | tomorrow it would help Nim turn the corner.
        
             | 0cf8612b2e1e wrote:
             | > Golang took off with a far weaker tooling story
             | 
             | All Go had to do was say, "Built by Google" and people came
             | running.
        
             | winrid wrote:
             | Maybe! It would get me on board, though :) I'm working on a
             | new server side project and am using Java for now, because
             | of this reason. I hope someday I have time to help build
             | IDE plugins and so on :)
        
       | pietroppeter wrote:
       | particularly relevant context for this post is that Nim v2 is
       | expected to be released within 2022, with the main change being
       | the arc/orc memory management discussed in the blogpost.
       | 
       | For a general review of what nim v2 will be about:
       | 
       | - Nim 2 video by Araq at https://nim-lang.org/nimconf2022/
       | 
       | - all the details about the goals of v2 are in the roadmap RFC
       | "Cylons are here" https://github.com/nim-lang/RFCs/issues/437
        
       | PMunch wrote:
       | Interesting to see more focus on embedded systems. This is
       | definitely good news for Ratel!
        
         | pietroppeter wrote:
         | which is this great project btw:
         | https://github.com/PMunch/ratel
         | 
         | what happened to that cool landing page you had?
        
       | [deleted]
        
       | nemo1618 wrote:
       | > Most of Nim's standard library collections are based on hashing
       | and only offer O(1) behavior on the average case. Usually this is
       | not good enough for a hard real-time setting and so these have to
       | be avoided. In fact, throughout Nim's history people found cases
       | of pathologically bad performance for these data structures.
       | Instead containers based on BTrees can be used that offer O(log
       | N) operations for lookup/insertion/deletion.
       | 
       | I'm surprised that hash maps can stray far enough from O(1) to
       | become unusable in hard real-time. Is it because RT systems have
       | to use lower-quality PRNGs?
        
         | CodesInChaos wrote:
         | When targeting hard real time you want strong worst-case
         | guarantees, not weak probabilistic arguments like "we hope the
         | hashes are distributed well enough to achieve O(1) in
         | practice".
         | 
         | The malicious version of this is hash DoS, where an attacker
         | chooses the values inserted into the hash table so they have
         | the same hash (or at least same bucket). Randomized hashing
         | using a secure hash mitigates this somewhat, but the attack is
         | still possible to some degree, especially if the hash secret is
         | long lived.
         | 
         | Another issue is that inserting into hash tables is usually
         | only amortized O(1), with the collection growing exponentially
         | when it's full. So most insert operations are cheap, but
         | occasionally you get an O(n) insertion. However it doesn't
         | sound like this is what the article is talking about.
        
           | cb321 wrote:
           | While I agree with all you said, one can expand a bit on it.
           | There are concrete max search depth expectations for well
           | randomizing hash functions such as:                   https:/
           | /www.sciencedirect.com/science/article/abs/pii/01966774879004
           | 0X
           | 
           | which is notably logarithmic - not unlike a B-Tree.
           | 
           | When these expectations are exceeded you can at least detect
           | a DoS attack. If you wait until such are seen, you can
           | activate a "more random" mitigation on the fly at about the
           | same cost as "the next resize/re-org/whatnot".
           | 
           | All you need to do is instrument your search to track the
           | depth. There is some example such strategy in Nim at
           | https://github.com/c-blake/adix for simple Robin-Hood Linear
           | Probed tables and a formula based one from that above paper
           | inside https://github.com/c-blake/suggest
           | 
           | This is all more about the "attack" scenario than the "truly
           | hard" real-time scenario, of course.
        
         | mungoman2 wrote:
         | The thing is that in this scenario you need to consider the
         | worst case access, not the average. So instead of O(1) accesses
         | you're stuck with O(n).
        
         | prirun wrote:
         | I did some work on Nim's hash tables back in 2020, specifically
         | with OrderedTable, comparable to a Python dict where insertion
         | order is preserved. I stumbled on this table module in a
         | roundabout way, via Nim's database module, db_sqlite. The
         | db_sqlite module was much slower than Python for simple tests,
         | and on investigation, I found that it didn't automatically
         | handled prepared statement caching like Python's sqlite3
         | module. There were some other issues with db_sqlite, like blob
         | handling and null handling, which led me to a different SQLite
         | interface, tiny_sqlite. This was a big improvement, handling
         | both nulls and blobs, and the developer was great to work with.
         | But it also didn't support prepared statement caching. I filed
         | an issue and he implemented it, using Nim's OrderedTable to
         | simulate an LRU cache by adding a new prepared statement and
         | deleting the oldest one if the cache was too big:
         | 
         | https://github.com/GULPF/tiny_sqlite/issues/3
         | 
         | Performance was hugely improved. There was another LRUCache
         | implementation I played with, and when using that for the
         | statement cache, performance was 25% faster than OrderedTable.
         | That didn't make much sense to me for a 100-entry hash table,
         | so I started running some tests comparing LRUCache and
         | OrderedTable. What I discovered is that OrderedTable delete
         | operations _created an entirely new copy of the table, minus
         | the entry being deleted, on every delete_. That seemed pretty
         | crazy, especially since it was already showing up as
         | performance problems in a 100-entry table.
         | 
         | The tiny_sqlite developer switched to LRUCache, and I did some
         | work on the OrderedTable implementation to make deletes O(1) as
         | expected with hash table operations:
         | 
         | https://github.com/nim-lang/Nim/pull/14995
         | 
         | After spending a lot of time on this, I finally gave up. The
         | problems were:
         | 
         | - the JSON implementation used OrderedTables and never did
         | deletes. JSON benchmark performance was rather sacred, so
         | changing OrderedTables to be slightly slower/larger (I used a
         | doubly-linked list) was not desirable, even if it changed
         | delete performance from O(n) to O(1)
         | 
         | - the Nim compiler also used OrderedTables and never did
         | deletes
         | 
         | - Nim tables allowed multiple values for the same key (I did
         | help get that deprecated).
         | 
         | - alternatives were proposed by others that maintained
         | insertion order until a deleted occurred, but then it could
         | become unordered. That made no sense to me.
         | 
         | The TLDR is, if you use Nim tables, don't use OrderedTable
         | unless you can afford to make an copy of the table on every
         | deleted.
         | 
         | Current Nim OrderedTable delete code: https://github.com/nim-
         | lang/Nim/blob/15bffc20ed8da26e68c88bb...
         | 
         | Issue for db_sqlite not handling nulls, blobs, statement cache:
         | https://github.com/nim-lang/Nim/issues/13559
        
         | tjoff wrote:
         | Having a better PRNG shouldn't help worst case should it?
         | 
         | And worst case is the only thing that matters in a hard real
         | time system.
        
         | [deleted]
        
         | jerf wrote:
         | Probably a better way of looking at it is hard real time is
         | _hard_ , in several senses of the term. You don't really want
         | any operations that have variable time at all. If that feels
         | like a really difficult specification to work with, it is. Soft
         | real time is more common. Soft real time theoretically has the
         | same considerations, but in general you have enough performance
         | that you can wave away many of these difficulties; yes,
         | technically my hash table is not 100% reliable every time but
         | as I can show it'll never have more than 128 entries, and I've
         | got 25ms targets for this task I'm doing, and I've got 32MB
         | when my code only uses about 4 max, and these numbers are all
         | large enough that I can just not care about the resizing
         | operation. I've got many systems I've written that are just
         | straight-up accidentally soft-real-time in practice simply
         | because running them on even a moderately-powerful modern PC is
         | a hundred times more power than they really need, and they're
         | already 10-50 times faster than they "need" to be even before
         | optimizing them, so barring pathological network performance
         | that would trash any system, they're essentially soft real time
         | already. You don't back your way into hard real time, though.
        
           | fluoridation wrote:
           | >I've got many systems I've written that are just straight-up
           | accidentally soft-real-time in practice simply because
           | running them on even a moderately-powerful modern PC is a
           | hundred times more power than they really need, and they're
           | already 10-50 times faster than they "need" to be even before
           | optimizing them, so barring pathological network performance
           | that would trash any system, they're essentially soft real
           | time already.
           | 
           | But the categorization of real-time depends on the
           | requirements of the problem, not how difficult it is to meet
           | them. If your problem tolerates a few missed deadlines every
           | now and then and your hardware is such that a naive
           | implementation misses deadlines so frequently that it's
           | useless, and so you need to optimize it thoroughly to get the
           | misses under control, it's still soft real-time. It doesn't
           | become soft real-time when technology advances enough that
           | even an inefficient implementation will tolerably a deadline
           | only occasionally.
        
       | christophilus wrote:
       | Good write-up. Optimizing for embedded, real-time systems is a
       | nice way for them to cut out a little niche among similar
       | languages.
       | 
       | Aside: I've tinkered with Nim a long time ago (when it was called
       | Nimrod) and thought it was pleasant. I'd be interested to hear
       | from anyone in the HN community that has used Nim in production.
       | What did you use it for? How was the experience?
        
         | themodelplumber wrote:
         | It runs part of my billing system and was nice to use,
         | particularly with community support which was fast and
         | effective given their online Nim tools.
         | 
         | Some of the debug / error messages were frustrating red
         | herrings which IIRC I was told was an area needing improvement
         | at least at the time.
        
         | nerdponx wrote:
         | I would gladly work at a company that used Nim as its primary
         | language.
        
         | vlugorilla wrote:
         | A well-known and nice app that is build with Nim is Nitter
         | (https://github.com/zedeus/nitter), a free and open source
         | alternative Twitter front-end focused on privacy and
         | performance.
        
         | calvinmorrison wrote:
         | I found it very pleasant as well! It was pythonesque that
         | compiled. Now pythons code makes me wants to pull my hair out.
         | I don't know what I like anymore
        
           | lordgroff wrote:
           | Nim is a Wirthian language not a Python language, but Python
           | always had some Wirthian touches so...
        
           | seanw444 wrote:
           | I've tried learning it a couple times, but the syntax seems
           | so irregular and non-uniform. It's confusing and annoying.
        
             | cb321 wrote:
             | Nim gives a bit more choice in many dimensions than many
             | languages -- how to manage memory, whether to use the
             | stdlib at all for things like hash tables, and yes, also
             | syntactic choices like several ways to call a function.
             | This can actually be convenient in constructing a DSL for
             | something with minimal fuss. While `func arg1 arg2` might
             | look weird in "real" "code", it might look great inside
             | some DSL and you can just have it be a "regular Nim
             | invocation" instead of something special to get that.
             | 
             | There are also compile-time superpowers like macros that
             | just receive a parsed AST. That can be used to "re-parse"
             | or "re-compile" external code as in
             | https://github.com/c-blake/cligen. So, trade-offs like in
             | all of life.
             | 
             | There is even a book called The Paradox Of Choice [1]. I
             | think there is just a spectrum/distribution of human
             | predisposition where some like to have things "standardized
             | & packaged up for them" while others more like to invent
             | their own rules..and enough variation within the population
             | that people have to learn to agree to disagree more. (EDIT:
             | and FWIW, I think this is context-modulated - the same
             | person could be on one end of the spectrum about gardening
             | and the other about software.)
             | 
             | I do feel like the syntax is far less chaotic than Perl.
             | 
             | [1] https://en.wikipedia.org/wiki/The_Paradox_of_Choice
        
             | olah_1 wrote:
             | I think this is because it takes more stylistic influence
             | from Oberon than it does from Python.
             | 
             | https://en.wikipedia.org/wiki/Nim_(programming_language)#In
             | f...
        
       ___________________________________________________________________
       (page generated 2022-11-11 23:01 UTC)