Live data from Hacker News

The case against a C alternative

c3.handmade.network

331–340 of 388 posts

Re: The case against a C alternative

#331

Earlier quoted context omitted.

Not to mention that both C++ and Rust can specialise algorithms and containers for specific types, whereas in C most developers resort to void* and function pointers. It's not unusual to see C programs written in a "typical" C style become dramatically faster when rewritten in a more modern language. For example, typical C programs also don't use hashtables even when this makes the most sense, causing weird performan…

What did you mean when you said C hashtables can’t be generic? Is the (void*) not an adequate solution?

There is no obvious way to get a hash from (void*).

And yes, C developers also tend to avoid ordered containers.

Re: The case against a C alternative

#332
post #194

Earlier quoted context omitted.

Not to mention that both C++ and Rust can specialise algorithms and containers for specific types, whereas in C most developers resort to void* and function pointers. It's not unusual to see C programs written in a "typical" C style become dramatically faster when rewritten in a more modern language. For example, typical C programs also don't use hashtables even when this makes the most sense, causing weird performan…

> it's just that C developers avoid them. I'm not sure it's fair to say C developers avoid hash tables - I've worked on several projects with hash-table implementations in them. The 'problem' if there is one, is that such things are rarely picked up from any sort of standard library, and are instead implemented in each project. I'm also not really sure what the problem is with 'resorting' to void*, it's part of the l…

C noob here. Why isn't a hash table implementation merged into the c standard library? Is it because the stdlib has to be as thin as possible for some performance reason or something?

Re: The case against a C alternative

#333
post #260

Earlier quoted context omitted.

C3: Currently being looked into https://github.com/c3lang/c3c#current-status Zig: https://ziglang.org/documentation/master/#Assembly Odin: Looks like it's a dead language, but it was on the docket. https://github.com/odin-lang/Odin/blob/master/misc/roadmap.m... Jai: https://github.com/Jai-Community/Jai-Community-Library/wiki/... eC: No clue. Not a great name for googling.

Odin is pretty much alive to JangaFX customers, https://jangafx.com/

Yup, I was just wrong on this. Their git project just got a commit yesterday. IDK what I was looking at that made me think it was defunct.

Re: The case against a C alternative

#334
post #80

> But aside from Jai, is anyone C alternative really looking to pursue having killer features? And if it doesn't have one, how does it prove the switch from C is worth it? It can't. Zig's `zig cc` is a killer feature that doesn't even require using Zig-the-language at all. `zig cc` is an LLVM-based C compiler that gives you trivial cross-compilation for existing C codebases, adds effective caching, and can be easily…

If you want to compete with Rust on cross compilation usability, you will have to do better than that.

Re: The case against a C alternative

#335

Earlier quoted context omitted.

> that has an acceptable tradeoff for performance vs space and simplicity for where they are used Is it? I've been programming strings for 45 years now. Including on 8 and 10 bit machines. All that space efficiency goes out the window when one wants a subset of a string that isn't a common tail. The simplicity goes out the window as soon as you want a substring that isn't a common tail. Now you have memory allocation…

Again, I'm not saying you should represent substrings, or strings in general for that matter, as zero terminated strings, and I'm not saying use zero terminated strings for anything longer than a couple bytes. No, I recommend everyone to use whatever fits the situation best. It might be a 2 byte start index and a 1 byte length fields that expresses the length as a multiple of 12 bytes. It might be rope data structure…

D doesn't have a builtin string type. A string in D is an array of characters. All arrays are length delineated.

> You will have to look quite a bit to find strlen() or strncpy() in my code. I'm not advocating for them, and not advocating to build serious string processing on top of zero-terminated strings.

Rolling your own string mechanism is simply not a strength of C. The downside of rolling your own is it is incompatible with everyone else's notion of how to avoid using 0 termination.

Re: The case against a C alternative

#336

Earlier quoted context omitted.

Again, I'm not saying you should represent substrings, or strings in general for that matter, as zero terminated strings, and I'm not saying use zero terminated strings for anything longer than a couple bytes. No, I recommend everyone to use whatever fits the situation best. It might be a 2 byte start index and a 1 byte length fields that expresses the length as a multiple of 12 bytes. It might be rope data structure…

D doesn't have a builtin string type. A string in D is an array of characters. All arrays are length delineated. > You will have to look quite a bit to find strlen() or strncpy() in my code. I'm not advocating for them, and not advocating to build serious string processing on top of zero-terminated strings. Rolling your own string mechanism is simply not a strength of C. The downside of rolling your own is it is inco…

I haven't even suggested to roll your own "string" type. Not more than rolling any other type of array or slice. In my programs I normally do not define a "string" type. Not a central one at least. Zero-terminated strings work just fine for the quick printf() or fopen().

Instead, I might have many string-ish types. A type to hold strings in the UI (may include layout information!), a type of string slice that points into some binary buffer, a rope string type to use in my editor, a fixed-size string as part of some message payload, a string-builder string that tries to be fast without imposing a fixed length... Again, there is little point in an "optimized" generic string type for systems programming, because... generic and optimized is a contradiction.

Re: The case against a C alternative

#337
post #141

Earlier quoted context omitted.

I agree. I think of address space context switching overhead as the performance price we pay for not being able to run all our programs in a single address space, which we could safely do if we knew all the programs were emitted by a trusted compiler that disallows unsafe memory access. Imagine if system calls were just ordinary functions that can be called with no more than the normal function call overhead? What if…

This is what Microsoft Research's Singularity OS did, with the language being C#. Their argument was that the MMU's address space isolation was a 30% Unsafe Code Tax so even if C# was slower than C if they could get the slowdown to less than that it was still an overall performance win. I'm pretty sure the discovery of Meltdown/Spectre and similar speculative execution attacks would completely wreck this model. The f…

Yeah, speculation attacks are a big problem. It seems like maybe if you're running a specific compiler you might be able to avoid speculation attacks by not emitting dangerous sequences of instructions, but I don't know what the state of the art is when it comes to Spectre mitigations and whether it's possible to have a compiler that can formally verify that a program is immune to any (known) speculation attacks.

Re: The case against a C alternative

#338

Earlier quoted context omitted.

You might be referring to slices. Arrays in rust have to have a known length at compile time.

Rustc may still need to do runtime bounds check if it can’t conclude that a dynamically computed index is in-bounds. The length is known statically, but whether the index is in-bounds may not be. The binary search GP talks about is exactly one such case, go on Godbolt, write up a simple binary search (with a static size so you get code), and you’ll see that the compiler checks against the literal array size on every…

I have no idea how I totally missed that from the GP's comment. Thanks for correcting me.

Re: The case against a C alternative

#339

Earlier quoted context omitted.

> become dramatically faster when rewritten in a more modern language IME that's mostly a myth though. A C compiler will stamp out a specialized version just as well if it can see all the relevant function bodies (either via inlining or LTO). "Zero cost abstraction" isn't just a C++ thing, it happens mostly in the language agnostic optimizer passes. For instance the reason why std::sort() shows up faster in benchmark…

inlining only goes so far. You won't get full of qsort to be inlined, and if it's not inlined, it needs to be at least cloned to be on par with std::sort, so the comparator function could get const-propagated. AFAIK out of the major compilers, gcc has the most aggressive cloning, but it's still nowhere near to const propagate the comparator from qsort. With std::sort with a stateless comparator function object (such…

glibc qsort's implementation is in libc.so, not in the header. GCC doesn't have anything to work with.

It's also an apples-to-oranges comparison, since std::sort and qsort implement different algorithms.

A lot of std::sort's performance is actually from using the version without any callbacks. If you pass a comparator function which just compares two integers the obvious way, it gets much slower. So one of std::sort's biggest advantages is actually not that it uses templates, but that it's specialized for the common case of not needing a custom callback. Theoretically the compiler should make the two cases the same, but apparently GCC is too dumb (that's not a slight on GCC; I think people expect too much from compilers):

  ------------------------------------------------------------------------
  Benchmark                              Time             CPU   Iterations
  ------------------------------------------------------------------------
  std_sort_random                 52881299 ns     52873089 ns           14
  std_sort_with_callback_random   63319633 ns     63307876 ns           11
  qsort_random                   106803314 ns    106784567 ns            7
  external_sort_random            97642851 ns     97640888 ns            7
  std_sort_sorted                  8433311 ns      8432564 ns           82
  std_sort_with_callback_sorted   13868016 ns     13865170 ns           50
  qsort_sorted                    28098439 ns     28093720 ns           26
  external_sort_sorted            33629020 ns     33628108 ns           21
external_sort is just std::sort hidden behind an extern function implemented in a separate .o file. Those benchmarks are from sorting 1MB of random and already-sorted data (as indicated in the names). I think it's important to test such cases, because often online people benchmark code which is written all in a single file, whereas real-life C++ projects are usually organized in such a way that every little class is in its own little file, which gets compiled into a separated object file, and then it all gets linked together without LTO. And then those same people go on to claim performance benefits of their language without actually using the setup which enables those benefits, which IMO is a bit dishonest.

When I drill further down into everything I want to drill into, maybe I'll publish the source for the benchmarks somewhere.

Re: The case against a C alternative

#340

Earlier quoted context omitted.

D doesn't have a builtin string type. A string in D is an array of characters. All arrays are length delineated. > You will have to look quite a bit to find strlen() or strncpy() in my code. I'm not advocating for them, and not advocating to build serious string processing on top of zero-terminated strings. Rolling your own string mechanism is simply not a strength of C. The downside of rolling your own is it is inco…

I haven't even suggested to roll your own "string" type. Not more than rolling any other type of array or slice. In my programs I normally do not define a "string" type. Not a central one at least. Zero-terminated strings work just fine for the quick printf() or fopen(). Instead, I might have many string-ish types. A type to hold strings in the UI (may include layout information!), a type of string slice that points…

Any length delineated string you're using, and you did say you were using length delineation, suffers from the problem of not being compatible with any other C code. There's a good reason operating system API calls tend to use 0 terminated strings.

If you want to do a quick debug printf() on it, well, you could use %.*s, but it's awkward and ugly (I speak from lots of experience). Otherwise, you gotta append the zero.

I'm not a C newbie. I've been programming C for 40 years now. I've written 2 professional C compilers, the most recent one I finished this year. When I started D, a major priority was doing strings a better way, as C ranks among the most inconvenient string processing languages :-)

Post reply on HN