HNHacker News
TopNewBestAskShowJobs

cb321

1,490 karma · joined July 25, 2020

submissionscomments
cb321··on Cligen: A Native API-Inferred Command-Line Interface Generator for Nim
There is actually a lot more in cligen/ than only CLI generation from proc signatures. https://c-blake.github.io/cligen/ covers some of that.
cb321··on Show HN: Scar – A programming language for easy concurrency and parallelism
FWIW, I almost always have to look at the generated assembly (never mind C) to even verify things are vectorized. I hope you realize the promise of a PLang where that doesn't need checking or some kind of "autovec-miss" compiler flags or so on and wish you all the best.

I'm curious if you have looked at Chapel: https://chapel-lang.org/

If not, you might find some thing there inspiring.

cb321··on Show HN: Scar – A programming language for easy concurrency and parallelism
I wouldn't reply except you mentioned this unboxing twice and I think people might get the wrong idea. I almost never use ref T/ref object in Nim. I find if you annotate functions at the C level gcc can even autovectorize a lot, like all four central moments: https://github.com/c-blake/adix/blob/3bee09a24313f8d92c185c9... - the relevance being that redoing your own BLAS level 1/2 stuff is really not so bad if the backend is autovectorizing it for you, and full SVD/matrix multiplying/factorizing can be a lot of work. Anyway, as per the link, the Nim itself is just straight, idiomatic Nim.

Parallel/threads is some whole other can of worms, of course. It is unfortunate that the stdlib is weak, both here and for numerics, and for other things, and that people are as dependency allergic as in C culture.

Anyway, "easier to optimize" is often subjective, and I don't mean to discourage you.

cb321··on Carbon Language: An experimental successor to C++
I can't speak to what libraries you used, but both techniques have been broadly used in common practice since the 1950s. According to the "History" subsection in Knuth TAOCP v3 at the very end of 6.4 (whose very first edition written in 1972 covered OA with various probe/collision strategy ideas), both open addressing & separate chaining were co-invented at IBM in 1953/54 by Luhn & Amdahl. You may be confusing the Celis 1985 Robin Hood hashing work with just the open addressing part? Anyway, as you say, it may be a strained example/analogy. "OO" & "concurrency" both have a lot going on, too.

Anyway, like the "major" modes of hash collision resolution, reference counted GC has also been around concurrently (haha) with ref tracing GC since the dawn of modern computing. Unix hard-links (& other things) codify ref counting into filesystems.. Python has always had ref-counted GC, older Lisp more focused on tracing GC, etc., etc. Popularity measures are notoriously difficult.

Mostly people like to abbreviate { like having a search $PATH instead of using /bin/foo everywhere }. The whole point of abstraction is to neglect details. Neglect naturally leads to forgetting (or never learning/knowing). Ignorance leads people to cross-talk (or worse willfully misinterpret/project). Cross-talk leads to suffering. Yoda out. ;-)

EDIT: Also, speaking of abbreviation & clarity, in Nim "arc" has, at least until this writing, always stood for Automatic Reference Counting, not Atomic Ref Counting as seems the more rusty terminology and is vaguely suggested by @miguel_martin, to whom I originally replied with an "arc/atomicArc", though it seems like, in Nim 3, it may become both Automatic & Atomic, but probably not changing its abbreviation to "AARC".

cb321··on Carbon Language: An experimental successor to C++
I see. So, it's just a slide-ware bullet point right now? It would be helpful to really emphasize a word like "planned" in that bullet. It might have been lifted from some future-sounding/plan-oriented list and now the material makes it seem like it's actually available.
cb321··on Carbon Language: An experimental successor to C++
We have zero disagreement here (actually true of all responses to my comment - an odd circumstance on HN). What you call "`ref` types" is what I meant by "GC'd types". I actually like that the Nim compiler changed from `--gc=X` to `--mm=X` a while back as the key distinction is (& has always been) "automatic vs. manual".

Elaborating on this cross-talk, any academic taxonomy says reference counting is a kind of GC. { See, the subtitle or table of contents of Jones 1996 "Garbage Collection: Algorithms for Automatic Dynamic Memory Management", for example. } Maybe you & I (or Nim's --mm?) can personally get the abbreviation "AMM" to catch on? I doubt it, but we can hope!! :) Sometimes I think I should try more. Other times I give up.

Before the late 90s, people would say "tracing GC" or "reference counting GC" and just "GC" for the general idea, but somehow early JavaVM GC's (and their imitators) were so annoying to so many that "The GC" came to usually refer, not just to the abstract idea of AMM, but to the specific, concrete separate tracing GC thread(s). It's a bit like if "hash table" had come to mean only a "separately chained linked list" variant because that's what you need for delete-in-the-middle-of-iterating like C++ STL wants and then only even the specific STL realization to boot { only luckily that didn't happen }.

cb321··on Carbon Language: An experimental successor to C++
Not to disagree, but to amplify - FWIW, most of what you say was also the sales pitch for C++ over ANSI C in the early 90s vs. the "pure Java" mentality that shortly followed in the late 90s (with a megaton of Sun Microsystems marketing to re-write almost everything rather than bridge with JNI). People neglect how practical incrementalism can be.

Also, FWIW, it is very ergonomic for Nim to call C (though the reverse is made complex by GC'd types). { I believe similar can be said for other PLangs you mention, but I am not as sure. } It's barely an inconvenience. Parts of Nim's stdlib still use libc and many PLangs do that for at least system calls. You can also just convert C to Nim with the c2nim program, though usually that requires a lot of hand editing afterwards.

Maybe they should write a C++2carbon translator tool? That would speed things up for them. Maybe they already have and I just haven't heard of it? I mean the article does say "some level of source-to-source translation", but I couldn't find details/caveats poking around for a few minutes.

cb321··on Asyncio: A library with too many sharp corners
This answer's @pansa2's question in a way that more leans on one aspect of Nim's greater syntactic & code transformation flexibility (which is great!), but I suspect pansa2 was more interested in differences in "idiomatic Nim" from Python for which there are more details here: https://github.com/nim-lang/Nim/wiki/Nim-for-Python-Programm...
cb321··on Serving 200M requests per day with a CGI-bin
Well, then you might like Nim for writing small CLI utilities. It just defaults to a console-friendly traceback on uncaught exceptions. cligen makes pretty nice CLIs pretty easy. (If you set up a $HOME/.cligen then you can get a shared colorized help / customization for any such utility, for example. So, set & forget kinda thing, maybe even making useful programs in as few as 3..7 lines of code, really.) It's not an experience without rough edges (though I'm not sure what is such an experience). So, probably depends on your priorities.
cb321··on Modernish – A library for writing programs for POSIX-based shells and utilities
Their `use` shell function should really be `use1` and `use` should process its whole argument list. Then their opening example could be `use safe sys/base` and "dialect tweaking" can be just one line with the option but not requirement to be multiple lines. Just a thought.
cb321··on Algorithms for Modern Processor Architectures
Well, like I said and you said - it depends on the calculations. :-) FWIW, I never meant to suggest he was wrong in any absolute sense, but I did do a double take reading that.

Besides the fine distinction you made about byte-at-a-time, one other way I sometimes try to express the variety is "BLAS L1/L2/L3". These levels roughly correspond to how much "loop nesting" there is, or roughly how well can we amortize the cost of memory transfers. So, L1 would be scaling each element by 10.0X, say, while L3 would be matrix multiply. One might have to be steeped in numerical linear algebra to appreciate that kind of analogy/terminology, though. Latency can also be a big issue vs. BW in "it all depends."

Ultimately, it comes down to "CPUs can do A LOT per clock cycle" these days - multi-way issue of up to 64-way vector instructions (byte-wise on avx512) and so on and what 1 cycle means can just vary by orders of magnitude (not even including multi-core orders of magnitude and then distributed orders of magnitude). But "not always". And it all depends. Part of CPUs getting so "big" is that the "play/drift" in various statements has a lot more flexibility. So, cross-talk about issues like this has also increased. As have, probably, double takes like I mentioned. :-)

cb321··on Algorithms for Modern Processor Architectures
This is a good set of slides. Dan is a good guy. There are a few nits I would pick. Sqrt(N) convergence comes from independence not normality -- based on independence => linearity of variance. { So, N IID samples of any distribution have a sum with N times higher variance, but then dividing by N you et sqrt(N). } There is, of course, a higher order relationship between the variance / "scale^2" of the distro and its tails which statisticians refer to as "shape". He later goes on to mention the dependence problem, though, and the minimum dt solution that, relied upon by, e.g., https://github.com/c-blake/bu/blob/main/doc/tim.md. So, it's all good. He may have covered it in voice, even.

He also mentions the Sattolo used by https://github.com/c-blake/bu/blob/main/doc/memlat.md to do his memory latency measurements. One weird thing was how he said because of 1 byte/cycle is 4GB/s things are "easily CPU bound" while I feel like I've been "fighting The Memory Wall for at least 3 decades now..." even just from super-scalar CPUs, but he later does some vectorization stuff. That more relates to what calcs you are doing, of course, but high bandwidth memory is a big part of what nVidia is selling.

cb321··on Speeding up my ZSH shell
Some data in support of @soraminazuki (who makes good points throughout this thread) on an i7-1370P linux laptop running Linux 6.15.7:

    $ tim 'bash -lic logout' 'zsh -lic logout'
    55.3 +- 3.7 μs  (AlreadySubtracted)Overhead
    7454 +- 26 μs   bash -lic logout
    7934 +- 39 μs   zsh -lic logout
(Using https://github.com/c-blake/bu/blob/main/doc/tim.md)

About 20,000 entries in the Zsh history slowing it a little. All completion activated (but zcompile'd, along with a very large digraphs.zwc).

I think most would not be troubled by taking 1.06X longer, though, especially at this 8 ms scale and for interactive session initiation. Note this test upper bounds Time To First Prompt { a different "TTFP" from Time To First Plot ;-) }, since it also exits as part of the `-c`.

cb321··on lsr: ls with io_uring
At those time scales, you would be better off using `tim` ( https://github.com/c-blake/bu/blob/main/doc/tim.md ) than hyperfine { and not just because that is your name! Lol. That is just a happy coincidence by clipping one letter off of the word "time". :-) } even though being in Nim might make it more of a challenge.
cb321··on Serving 200M requests per day with a CGI-bin
I should have also said, I suppose, that cgitb is less needed by Nim because the static typing catches more errors at compile-time. Indeed, for CGI programs, it might be wise to use the `{.raises: [].}` pragma to forbid successfully compiling if you even could raise at all, formally obsoleting any utility cgitb would have had. All just informational if anyone is tempted to write CGI programs in Nim. :-)
cb321··on Phrase origin: Why do we "call" functions?
Yeppers. For a few more details, one might start here: https://en.wikipedia.org/wiki/Universal_quantification
cb321··on Serving 200M requests per day with a CGI-bin
Thanks!
cb321··on Serving 200M requests per day with a CGI-bin
Do you have any URIs or back up links? Encouragement to actively break compatibility sounds like "inciting murder" vs. a more tame "don't expend effort (go to medical school)" to save folks.

{ Oh, and before anyone jumps on me, this is only an analogy as it relates to freshman moral philosophy courses, not an attempt by me to over-dramatize - that is more the fault of said courses trying to engage 18 year olds. :-) I'm mostly interested in the active-passive details of the pledge campaign. }

cb321··on Serving 200M requests per day with a CGI-bin
Yeah. I mentioned this the other day: https://news.ycombinator.com/item?id=44466245

Combined with the dot-com boom "general hype", I'm sure a lot of managers pushed heavyweight solutions where lightweight would have sufficed. Well, that may be an eternal problem, but maybe more succeeded in pushing them with a lot of hype. :-)

Not enough people I guess saw this as Sun trying to be the new Microsoft (which was the new IBM, which still has MVS & Cobol!), namely the company in control of The Platform, where here "The" just means the hip new thing kids learn in school and want to continue doing before they become expensive old timers.

cb321··on Serving 200M requests per day with a CGI-bin
I'm not sure what @LtWorf means, exactly, but one reason I can think of is that on Linux (Gentoo & Debian at least), the system package managers are not putting pip in place by default with python itself, the way they used to. The rationale, I believe, is to steer users towards using the system package manager or only doing ~/.local style things.

EDIT: So, you get threads like this https://stackoverflow.com/questions/65651040/what-is-the-rec... and so on

cb321··on Serving 200M requests per day with a CGI-bin
That article is a pretty good overview at the time.

Only one benchmark on one system, but over in day before yesterday's HN thread on this (https://news.ycombinator.com/item?id=44464272), I report a rather significant slowdown in Perl start up overhead: https://news.ycombinator.com/item?id=44467268 . Of course, at least for me, Python3 is worse than Python2 by an even larger factor and Python2 worse than Perl today by an even larger factor.

FWIW, in Nim, you can get a CGI that probably runs faster than the Go of this article with simply:

    import std/cgi                  # By default both ..
    for (key, val) in decodeData(): #.. $QUERY_STRING & POST
      if key == "something":
         do_something(val)
I don't know of a `cgitb` equivalent even in the Nimbleverse. Some of the many web frameworks in Nim like jester seem to have that kind of thing built into them, though I realize a framework is not the same as CGI (and that's one of the charms of CGI).
cb321··on Serving 200M requests per day with a CGI-bin
Well, I spent a little more time on it, and for that one Perl version (on one cpu, i7-6700k, one OS - Linux 6.15, with one compiler gcc-15) I got a 1.5x slowdown of perl startup from 2008 to 2025. Not great!

    zsh# rei=(chrt 99 taskset -c 2-3 env -i HOME=$HOME PATH=/bin)
    zsh# tim=(-k2 -o14 -n14 -m14)
    
    # Set 1
    zsh# $rei tim $tim './perl-5.8.9</n' 'perl-5.42.0</n'
    218.5 +- 1.9 μs (AlreadySubtracted)Overhead
    626 +- 14 μs    ./perl-5.8.9</n
    953.5 +- 8.6 μs perl-5.42.0</n
    
    # Set 2
    zsh# $rei tim $tim './perl-5.8.9</n' 'perl-5.42.0</n'
    247 +- 15 μs    (AlreadySubtracted)Overhead
    633 +- 26 μs    ./perl-5.8.9</n
    989 +- 25 μs    perl-5.42.0</n
    
    # Consistency check - well within 2 sigma for both
    zsh# a (989 +- 25)-(953.5 +- 8.6)
    36 +- 26
    zsh# a (626 +- 14)-(633 +- 26)   
    -7 +- 30
    
    # Final report; weighted means to maximize precision
    zsh# a mean(989 +- 25,953.5 +- 8.6)/mean(626 +- 14,633 +- 26)
    1.525 +- 0.033
EDIT: Oh, yeah, and /n -> /dev/null is a symlink I always use.
cb321··on Serving 200M requests per day with a CGI-bin
The major modern use case I know of is command-line utilities which also benefit from low start-up. Of course, that doesn't mean there hasn't been "perf rot" over the decades as you say. Such rot should never surprise anyone. :-)

Some perl5 lover should take the time to compile all those 5.6 to 5.42 versions on the same host OS/CPU and do a performance comparison and create a nice chart for the world to trap and maybe correct such performance regressions. I just tried getting 5.8.9 to compile on modern Linux with gcc-15, and it seemed like a real PITA. (Earlier didn't even ./Configure -des right.)

cb321··on Serving 200M requests per day with a CGI-bin
Besides already agreeing to disagree on the Java slogan & syntax, I agree with all you say. Familiarity/being taught in schools is for sure a huge deal. { Not really "news" - Steve Jobs used to give high schools Apple IIs for the same reason. :-) } When even Scheme-inventing MIT moved to Python the writing was on the wall.

I've always been a little surprised Nim wasn't more popular with its Python like syntax, Lisp like meta-power, C-like speed and usually automatic memory management. Ah well.

cb321··on Serving 200M requests per day with a CGI-bin
While all you say is true, it bears note that it didn't need to be decisive. The current mob branch of tcc is such that a `#!/bin/tcc -run` "script" is about 1.3x faster than perl</dev/null on two CPUs I tried.

Besides your two slower examples, Julia and Java VMs and else thread PHP also have really big start up times. As I said up top, people just get addicted to "big environments". Lisp culture would do that with images and this is part of where the "emacs is bloated" meme came from.

Anyway, at the time getline wasn't even standardized (that was 2008 POSIX - and still not in Windows; facepalm emoji), but you could write a pretty slick little library for CGI in a few hundred..few thou' lines of C. Someone surely even did.

But things go by "reputation" and people learn what their friends tell them to, by and large. So, CGI was absolutely the thing that made the mid to late 90s "Perl's moment".

cb321··on Serving 200M requests per day with a CGI-bin
Ah. You refer to the "write once, run anywhere" marketing slogan at a time when there was a lot of JVM-to-JVM variability and JVM JITs were not very advanced. I didn't buy that slogan at the time (look at aaaaall that open source code running all over with ./configure) and always hated Java's crazy boilerplate verbosity combined with a culture to defend it as somehow "better".

I mean, it's not like people ran Internet servers on such vastly different CPUs/OSes or that diversity was such a disadvantage. DEC Alpha was probably the most different for its 64-bitness, but I ran all those open source Linux/C things on that by 1996..97. But we may just have to agree to disagree that it made a lot of sense for that reason. I have disagreements with several high profile SiValley "choices" and I know I'm a little weird.

Anyway, I don't mean to be arbitrarily disputatious. Focusing on what we do agree on, I agree 100% early Java stdlib's being bigger than C/C++ & early STL/template awfulness was a huge effect. :-) C++ like keywords and lexical sensibilities mattered, too. PLang researchers joked upon Java's success that C's replacement sure had to "look like C". But I think programmers having all their lib needs met with very little work matters even more and network-first package managers were just getting going. A perceived-as-good stdlib absolutely helps Go even today. Human network effects are a very real driver even among very talented engineers.

Maybe since CTAN/CPAN?, that often comes more from popularity and the ecosystem than "what's in the stdlib". Even before then there was netlib/fortran algorithm distribution, though. Node/Rust/Python worlds today show this.

How to drive popularity in a market of ideas competing for it is hard/finicky or the biggest marketing budget would always win and people could also always just "buy reputation" which empirically does not happen (though it sure happens sometimes which I guess shows ad spend is not wasted). Even so, "free advertising", "ecosystem builds going exponential", etc. - these are just tricky to induce.

The indisputable elephant in the room is path dependence. Fortran, still somewhat reflective of people "stacking punch card decks" to "link" programs in the 1950s, is still used by much modern scientific research either directly or indirectly. Folks were literally just figuring out what a PLang should be and how interacting with these new things called "computers" might work.

But path dependence is everywhere all around us.. in institutions, traditions, and technology. It's all really a big Humanity Complete discussion that spirals into a cluster of Wicked Problems. Happens so fast on so many topics. :-) If you happen to make something that catches on, let us hope you didn't make too many mistakes that get frozen in! Cheers!

cb321··on Serving 200M requests per day with a CGI-bin
This is not to disagree, but to agree adding some detail... :-)

Part of the Java rise was C/C++ being error prone and syntax similarity with such, but this was surely intermingled with a full scale marketing assault by Sun Microsystems who at the time had big multi-socket SMP servers they wanted to sell with Solaris/etc. and part of that was the Solaris/Java threading. Really for a decade or two prior to that the focus was on true MMU-based hardware-enforced isolation with OS kernel clean-up (more like CHERI these days) not the compiler-enforced stuff like Rust does.

I think you could have something more ergonomic than Perl/Python ever was and as practically fast as C/Rust with Nim (https://nim-lang.org/). E.g., I just copied that guy's benchmark with a Nim stdlib std/cgi and got over 275M CGI/day to localhost on a 2016 CPU doing only 2 requesters & 2 http server threads. With some nice DSL easily written if you don't like any current ones you could get the "coding overhead" down to a tiny footprint. In fairness I did zero SQLite whatever, but also he was using a computer over 4x bigger and probably a GHz faster with some IPC lift as well. So, IF you had the network bandwidth (hint - usually you don't!), you could probably support billions of hits/day off a single server.

To head off some lazy complaints, GC is just not an issue with a single threaded Nim program whose lifetime is hoped/expected to be short anyway. In many cases (just as with CLI utilities!) you could probably just let the OS reap memory, but, of course, it always "all depends" on a lot of context. Nim does reference counting anyway whereas most "fighting the GC" is actually fighting a "separate GC thread" (Java again, Go, D, etc.) trashing CPU caches or consuming DIMM bandwidth and so on. For this use, you probably would care more about a statically linked binary so you don't pay ld.so shared library set up overhead on every `exec`.

cb321··on Serving 200M requests per day with a CGI-bin
Even back in the 1990s, CGI programs written in C were lightning fast. It just was (is) an error prone environment. Any safer modern alternative like the article's Go program or Nim or whatever not making database connections will be very fast & low latency to localhost - really similar to a CLI utility where you fork & exec. It's not free, but it's not that expensive compared to network latencies then or now.

People/orgs do tend to get kind of addicted to certain technologies that can interact poorly with the one-shot model, though. E.g., high start up cost Python interpreters with a lot of imports are still pretty slow, and people get addicted to that ecosystem and so need multi-shot/persistent alternatives.

The one-shot model in early HTTP was itself a pendulum swing from other concerns, e.g. ftp servers not having enough RAM for 100s of long-lived, often mostly idle logins.

cb321··on I write type-safe generic data structures in C
> look at the major standard libraries for hash tables

I don't know what would count as "major", but at least to me "major" does not imply "good". As I mentioned, this idea is ancient (the point of citing Knuth from over half a century ago - that was me, not @dwattttt) but also weakly disseminated. In said weakness, it may well be uncommon in "major" impls, but that merely recapitulates my point. Honestly, that and the resizing thing was such weird argumentation that I didn't take the time to reply.

One does usually resize on insert to keep things sparse as @dwattttt mentioned. With deletions, might want to resize after delete to keep things dense which could help if you iterate over the table much after deleting a lot of it. That is not a cost you would ever pay if you don't delete. So, ability to delete is still something insert-only workloads don't pay anything for.

Moving past the off-point qualification of "major"-ness & weird resize points, if you are actually curious about more details how this can work and want a concrete example, you could look at the Nim stdlib Table implementation: https://github.com/nim-lang/Nim/blob/fbdc9a4c19aafc25937aaa5... or you could read the Knuth subsection that I referenced, but Knuth uses a rather archaic linear probing to smaller indices which probably seems unnatural to modern readers. There is nothing special added to the insert-only data/pathways to support deletes either in that Nim code or the Knuth.

Deletes done this way can benefit from saving integer hash codes (and that happens to be done in the Nim example), but so do all finds via prefix compare & resizes by avoiding hash(). So, in the space-time trade-off analysis of "is saving hash codes worth it?", delete finds & backshifts are just one more part of some workload. This is "deletes in a workload impact space-time trade-offs", though, not "ability to delete costs insert-only workloads" which seems to be the contentious point.

cb321··on I write type-safe generic data structures in C
This is true of the common tombstone approach to deletions in hash tables { which also require rehashing (like in resizing) if there are too many tombstones }.

Somehow dissemination of Knuth v3,chapter6.4 Algorithm D has been weak, though it has lately become known as "backshift deletion" - unsure who coined that. It is limited to linear probing (but then these days that also creates the least memory traffic/potential latency). With this approach, there is no real specialized form of "hash tables that can delete". You may already know all of this and not disagreeing with anything. Just a natural follow-up.

← PreviousPage 5 of 29Next →