HNHacker News
TopNewBestAskShowJobs

jstimpfle

4,224 karma · joined May 21, 2015

http://jstimpfle.de
submissionscomments
jstimpfle··on Everything in C is undefined behavior
But did the rust compiler assume that the integer would not overflow? It did so in Debug mode where runtime checks were added. If it's not the case in Release mode, does that mean semantics are different between Debug and Release?
jstimpfle··on Everything in C is undefined behavior
Then I'm almost ashamed to admit that I'm not sure I've ever witnessed any surprising form of UB in the wild. For example, I will reliably get segfaults on NULL dereference in practice. Typical manifestations of UB are entirely predictable and obvious. Of course I'm also running most code without most optimizations, most of the time, while developing.

On the other hand, what I've observed with my own eyes is interesting phenomenons like performance drops, e.g. memory bandwidth dropping from gigabytes/sec to 300 KB/sec due to false sharing on an ARM SOC for example.

jstimpfle··on Everything in C is undefined behavior
That's true, and that's why your typical string vector code has a prelude and a postlude to do the incomplete chunks at the ends. Between the ends, it's processing larger self-aligned chunks.
jstimpfle··on Everything in C is undefined behavior
A program with corrupted state can essentially do anything. Yes it's still a question of run-time checks the runtime has to protect against it. But the compiler is probably deriving a lot of assumptions from the assumption that there wasn't overflow.
jstimpfle··on Everything in C is undefined behavior
What if the wrapped index is used to construct an invalid pointer? It might be possible, not sure. What if the integer is used to read the wrong data from disk, or corrupt data on disk by writing to the wrong location?
jstimpfle··on Everything in C is undefined behavior
Where is the part about unaligned pointers?
jstimpfle··on Everything in C is undefined behavior
*is not related to C flexibility
jstimpfle··on Everything in C is undefined behavior
I understand, that's why I said the code is obviously broken. The problem is not about order of evaluation. It's not about an UB arising from unsequenced volatile reads or whatever.

The problem is simply that the there are two volatile reads where only one was intended. It doesn't matter if there is UB or not. The code doesn't express the intention either way. All you need to know to understand that is that volatile might be modified concurrently (a little bit similar but not the same semantics as atomics).

jstimpfle··on Everything in C is undefined behavior
IIRC computation of the address is done by computing offset from base pointer as a multiplication in (32-bit) int, (like p + (i * sizeof (Foo)). The right term might overflow, but due to signed overflow being UB, the compiler is able to assume that it does not, so the transformation to do the arithmetic entirely in (64-bit) pointer space is valid.
jstimpfle··on Everything in C is undefined behavior
Of course it's optional (although I didn't mean to imply that). Even using computers at all is optional. I never said that I don't aim to follow the standard, have a clean compiling program without warnings and without UB, etc. I do strive to achieve all of that.

But it's not entirely black and white, either. In practice I'm fine accepting that some bugs are technically UB but whatever, we've found a bug by whatever manifestation (like NULL dereference most likely leading to segfault in practice). I just fix the bug as a bug, and life goes on.

The standard is not perfect, it does have shortcomings. It can be improved. And it can be interpreted to fix some issues. Let's not hold theory over practicality, and let's expect the compiler writers also strive to do the reasonable thing.

jstimpfle··on Everything in C is undefined behavior
If it is defined as an error, but the compiled build will continue to run with the value wrapped around, I would say that's indistinguishable from UB.
jstimpfle··on Everything in C is undefined behavior
This string manipulation stuff is very common, and that's why in 2026, an age where science fiction has become a reality, many things are still absurdly slow. Exactly because of such sloppiness, which does accumulate in many cases, and when one least expected it.
jstimpfle··on Everything in C is undefined behavior
Where did I say I'm not following the standard?
jstimpfle··on Everything in C is undefined behavior
You can run your code under ASAN and UBSAN nowadays, it will catch many or most of issues as they happen.

But that's completely besides the point. UB on signed overflow, or really most of UB, is not unrelated to C flexibility. It is a detail of the spec related to portability and performance. IIRC it is even required to make such trivial optimizations as turning

    for (int i = 0; i < n; i++) func(a[i]);
into

    for (Foo *p = a, *last = a + n; p < last; p++) func(p);
saving arithmetics and saving a register, on architectures where `int` is smaller than pointers. But there is also options like -fwrapv on GCC for example, allowing you to actually use signed overflow.
jstimpfle··on Everything in C is undefined behavior
what is your point?
jstimpfle··on Everything in C is undefined behavior
I'm making the choice to pass pointers as void to get low-friction polymorphism. I'm making the choice to control the memory layout of my data structures, including of levels and type of indirection. I'm making the choice to control my own memory allocators and closely control lifetimes, closely control (almost) everything that happens in the system.

That has nothing to do with not following the standard.

jstimpfle··on Everything in C is undefined behavior
I would agree that C is "really flexible", but I would say it's primarily flexible because it lets you cast say from a void pointer to a typed pointer without requiring much boilerplate. It's also flexible because it lets you control memory layout and resource management patterns quite closely.

If you want to be standards correct, yes you have to know the standard well. True. And you can always slip, and learn another gotcha. Also true. But it's still extremely flexible.

jstimpfle··on Everything in C is undefined behavior
Not sure why you're being downvoted. That's completely right. The example is silly. The code is obviously bad, doesn't matter if it's UB or not.

I'm also not convinced (yet) that the example really is UB: I agree reading a volatile is "a side effect" in some sense, and GP cited a paragraph that says just that. But GP doesn't clearly quote that it's a side effect on the object (or how a side effect on an object is defined). Reading an object doesn't mutate it after all.

But whatever language lawyer things, the code is obviously broken, with an obvious fix, so I'm not so interested in what its semantics should be. Here is the fix:

    volatile int x;
    // ...
    int val = x;  // volatile read
    printf("%x %d\n", val, val);
jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
It really depends on what you are doing. Sometimes I am not building in a day, just designing and thinking by editing. Sometimes I am refactoring for hours without building. But sometimes I am rebuilding every 10 seconds or so.
jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
Not slow to compile? 0,833 seconds extra compile time for a trivial utility class that doesn't do anything interesting other than make something perceived "safer"? Does that mean that each of the 911 instantiations took several million CPU ticks? You could convince me that it's not slow if it was 2-4 orders of magnitude less.

As I wrote elsewhere, 1 second is a timespan where we could aim to compile 1 MLOC of code on a single core.

> A more general use case is just reducing code repetition in a type-safe manner

As I said -- code reuse. And interestingly your Optional.hpp is a header...

jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
Erm... that's not just false. The point of templates is generic programming, reusable components. If you don't put them in a header, you're not reusing them much. And if you have to "selectively pick TUs where they're instantiated", you're basically admitting that you have to invest effort to reduce compile times. You are refuting the very point you're making.

C++ templates _are_ slow to compile. They require running something like a dynamically typed VM in the compiler.

jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
Many C++ features are very slow to compile, especially templates.

A quick compiling C++ project is most likely extremely conservative in its use of C++ (vs C) features.

jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
This 1000%, sorry for low calories comment.
jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
Running the code immediately after making changes is the first line of testing. To run a huge test suite full of tests that are completely unrelated to the current changes would be stupid, it's a huge waste of time and energy.

Yes, seriously, have you ever written a project from scratch? A simple .c file with a thousand lines in it should easily build and start within 100ms. A compiler should be able to do basic parsing and codegen at 1M lines per core.

If your runs take 48h, of course you need a strategy to avoid noticing bugs only after dozens of hours running. You can't tell me that it is efficient to make changes and to wait for minutes or even hours before noticing that your code wasn't even syntactically valid, or maybe it did compile but your code had a small oversight and you need to start over building.

The Linux kernel is a HUGE project, one of the biggest around. Yes, a full rebuild takes a long time, depending on configuration. Incremental rebuilds do not, though.

I'm actually working on a Linux kernel module (distributed filesystem client), it's on the order of 40 KLOC. I can do a full rebuild in 10/15 seconds (debug/release), and that includes calling into the kernel's infrastructure and doing a lot of stuff that shouldn't have to be done. An incremental rebuild after changing a single .c file is about 3 seconds. Restarting the module (swapping for the newly built one) takes less than 10 seconds also. And this can be already a stressful bottleneck depending on the task. Say you're improving logging in a particular section of code, this can easily require 5-10 attempts.

I'm working on Desktop GUIs (2D/3D) too. You need a quick turnaround time as much as possible. Many changes are trivial but you want to do many small incremental improvements, recompile, run and test (manually), often with a breakpoint on the code you're currently working on.

The projects I'm working on are written in C or conservative C++, and most have from thousands to hundreds of thousands lines of code. They can be built from scratch in a short amount of time (< 10s for the smaller ones). And all of them do incremental builds in <= 10 seconds except when maybe changing the most central headers which essentially means a full rebuild.

You can also design a C/C++ codebase to always do a full rebuild, compiling everything as a single unit. That can be faster than trying to do incremental builds, for codebases of considerable size. Try out the popular raddebugger project, a complete build after checkout is about 3 seconds. It's ~300 KLOC I think.

jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
So every time you compile, you run your test suite? I don't. And you trust that I have experience writing and compiling programs too...?

It should be a goal to keep rebuild times around 1 second (often not quite possible, but 3-5 seconds, even for full rebuilds, is often realistic). I edit, compile, run, edit, compile, run. Editing and running can often take as little as 1-3 seconds, and I sometimes do it dozens of times working in a row, working on a single improvement. That's why there is a 1 second rebuild time goal.

In practice I often work on codebases I don't fully control, but when the build times are excessively high, I will complain and try to improve. Build times longer than 10-15 seconds break the flow, they are a significant productivity hit. But they are quite common with C++ codebases (it can also be bad with C codebases by the way, but C++ is typically much worse because of templates and metaprogramming which is very slow).

> Compilation times don't even measure.

You must be joking. Do you even program?

jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
If you need callbacks and generics, you're not writing performance code.

99% of code in the wild is comically inefficient and is doing the wrong thing, using way too generic data structures and algorithms for very concrete problems. C++ templates may be one way to make comically slow code faster by spending a lot of compile time. But it's often much quicker to just write straightforward concrete code that the compiler can easily optimize.

IMO C++ makes for slow programs for the sole fact that it compiles so slow (if you use its modern features), so you have much less time to actually iterate and improve.

jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
C is not slow compared to C++, that is a strange myth.
jstimpfle··on Cost of enum-to-string: C++26 reflection vs. the old ways
Actually why even specify metaprogram as C like source code? It must be convenience. But there is little practical use, like a good program always models a lot of different representations of more or less the same things, just recombined and processed a little differently. Why would we want to deal with semantics of C types for example, if we can model a much clearer and better constrained universe of types used in e.g. a de/serialization framework? Even only pointers are quite special, and often only of very immediate use, but there is no point in e.g. persisting them to disk or sending them over the network.
jstimpfle··on Linux Terminal Memory Usage
> I probably wouldnt have tested it

(if not for a nerdy sysadmin/hacker person that introduced me to all of that at the time). Was typing on smartphone half-asleep...

jstimpfle··on Linux Terminal Memory Usage
It would probably have been the best alternative. I remember it felt similar to xterm, wasn't bloated as many others in the way I described. I don't remember why exactly it didn't stick for me, also don't remember any things it did better. One reason might have been compatibility -- out of the box, most programs work quite well with xterm.
← PreviousPage 7 of 34Next →