Prototyping in C

post by jefftk (jkaufman) · 2022-09-04T17:50:01.565Z · LW · GW · 11 comments

Lately I've been writing some new (" greenfield") code in C. I feel a bit silly about this: isn't Rust what you're supposed to use if you want metal-level performance and are unconstrained by past engineering decisions? Or Go? Something on the JVM? Even C++? Why use a 50 year-old language that is notorious for memory unsafety and nasal demons?

There are a few things about this situation that are a pretty good fit for C:

This is a bit of an unusual confluence of factors, and if you'd asked me a few years ago if I was ever going to write C professionally again, let alone choose to start something in C, I would have said no. Yet, in this case, I think it's the right call.

(Large parts of my rhythm stage setup, including both the MIDI routing and whistle-controlled synthesizer are also in C, though there for minimizing latency instead of maximizing throughput. Since that's something silly I'm doing for fun I feel less weird about it.)

Comment via: facebook

11 comments

Comments sorted by top scores.

comment by Lorenzo (lorenzo-buonanno) · 2022-09-04T20:30:26.501Z · LW(p) · GW(p)

Probably dumb questions:

 - Have you tried PyPy? It might increase the number of cases where python is good enough.

 - Why C instead of C++? (I assume Rust would slow down prototyping speed because of the borrow checker). Is it because you're more familiar with it from your side projects?

Replies from: jkaufman, cooljoseph1
comment by jefftk (jkaufman) · 2022-09-05T23:52:23.652Z · LW(p) · GW(p)

Have you tried PyPy?

I haven't tried PyPy for this particular project, but my experience previously had been that while I usually got a bit of speedup it wasn't typically much.

Why C instead of C++?

I used to work in C++ and know it pretty well, but I don't really like it very much and there isn't anything in C++ I've needed here.

Replies from: lorenzo-buonanno
comment by Lorenzo (lorenzo-buonanno) · 2022-09-06T12:45:54.175Z · LW(p) · GW(p)

Thanks for the reply!

I haven't tried PyPy for this particular project, but my experience previously had been that while I usually got a bit of speedup it wasn't typically much.

That's also my experience in most cases, but in others can be much faster. It does especially well on code with lots of looping that can be JITted.
As a data point, https://github.com/jeffkaufman/kmer-egd/blob/main/count-quality.py is ~3x faster on my machine (takes 0.8 seconds vs 3 seconds on 100k lines of length 151 containing FF).
Which probably is not enough to make a difference, but might still be useful.

Replies from: jkaufman
comment by jefftk (jkaufman) · 2022-09-06T15:23:48.939Z · LW(p) · GW(p)

Yes, probably not enough to make a difference; that one in particular is fast enough in python. But useful to have the number!

comment by joseph_c (cooljoseph1) · 2022-09-06T03:36:17.991Z · LW(p) · GW(p)

You can also use Numba to speed up loops.  It's still slower than C, but it's much better than plain Python code, and it's really easy to implement (just import  numba and put a @numba.njit() before your function).

comment by Tomás B. (Bjartur Tómas) · 2022-09-05T15:31:01.171Z · LW(p) · GW(p)

I'm curious if you looked into any of the "better C" languages that have come out lately -  Odin/Jai/Zig. 

comment by shminux · 2022-09-04T20:07:24.547Z · LW(p) · GW(p)

C with some basic object support (single inheritance, interfaces) and a better/safer string manipulation library (all mutable strings are structs with current/max size, no null-termination) and without implicit new/free of C++ is what I miss in most of my C projects, and have to recreate or import on any new greenfield project. There are probably C frameworks out there that give you that. Goes a long way toward a safer C without performance overhead of managed code. 

comment by __nobody · 2022-09-05T14:41:10.272Z · LW(p) · GW(p)

The way I approach situations like that is to write code in Lua and only push stuff that really has to be fast down to C. (Even C+liblua / using a Lua state just as a calling convention is IMHO often nicer than "plain" C. I can't claim the same for Python...) End result is that most of the code is readable, and usually (i.e. unless I stopped keeping them in sync) the "fast" functions still have a Lua version that permits differential testing.

Fundamentally agree with the C not C++/Rust/... theme though, C is great for this because it doesn't have tons of checks. (And that's coming from someone who's using Coq / dependent types regularly, including at work.) Compilers generally want to see that the code is safe under all possible uses, whereas you only care about the specific constellations that you actually use when prototyping. Convincing the compiler, disabling checks, and/or adding extra boilerplate adds overhead that seriously slows down the process of exploration, which is not something that you want to deal with in that mode.

comment by Derek M. Jones (Derek-Jones) · 2022-09-04T18:41:15.318Z · LW(p) · GW(p)

Why aren't you using the various C compiler options that do all the pointer/array bounds checking that the Rust fan bois think are unique to them?

Plus there are the integer overflow checking options that Rust eventually got around to supporting.

Replies from: jkaufman
comment by jefftk (jkaufman) · 2022-09-04T19:57:07.787Z · LW(p) · GW(p)

Probably worth doing! Recommendations?

Replies from: Derek-Jones
comment by Derek M. Jones (Derek-Jones) · 2022-09-04T20:31:09.826Z · LW(p) · GW(p)

I'm a long-time hardcore bounds-checking fan.

Others prefer: -fsanitize=address,undefined,bounds-strict