[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)