HNHacker News
TopNewBestAskShowJobs

jstimpfle

4,224 karma · joined May 21, 2015

http://jstimpfle.de
submissionscomments
jstimpfle··on Java FFM zero-copy transport using io_uring
I was really asking what "IPC" means in this context. If you can just share a mapping, yes it's going to be quite fast. If you need to wait for approval to come back, it's going to take more time. If you can't share a memory segment, even more time.
jstimpfle··on Java FFM zero-copy transport using io_uring
Asking for those who, like me, haven't yet taken the time to find technical information on that webpage:

What exactly does that roundtrip latency number measure (especially your 1us)? Does zero copy imply mapping pages between processes? Is there an async kernel component involved (like I would infer from "io_uring") or just two user space processes mapping pages?

jstimpfle··on COM Like a Bomb: Rust Outlook Add-in
I'd say COM is also run-time type safe casting, and importantly the reference counting is uniform which might help writing wrappers for dynamic and garbage collected languages.

I'm still not sure that it brings a lot to the table for ordinary application development.

jstimpfle··on Rethinking C++: Architecture, Concepts, and Responsibility
_Any_ code that was never previously exercised could be wrong. printf() calls are typically typechecked. If you write wrappers you can also have the compiler type check them, at least with GCC. printf() code is quite low risk. That's not to say I've never passed the wrong arguments. It has happened, but a very low number of times. There is much more risky code.

So such a strong "at the very least" is misapplied. All this template crap, I've done it before. All but the thinnest template abstraction layers typically end up in the garbage can after trying to use them for anything serious.

jstimpfle··on Rethinking C++: Architecture, Concepts, and Responsibility
I'll bite. printf might be unsafe in terms of typing, in theory, but it's explicit and readable (with some caveats such as "PRIi32"). The actual chance of errors happening is very low in practice, because format strings are static in all practical (sane) uses so testing a single codepath will usually detect any programmer errors -- which are already very rare with some practice. On top of that, most compilers validate format strings. printf compiles, links, and runs comparatively quickly and has small memory footprint. It is stateless so you're always getting the expected results.

Compare to <iostream>, which is stateful and slow.

There's also std::format which might be safe and flexible and have some of the advantages of printf. But I can't use it at any of the places I'm working since it's C++20. It probably also uses a lot of template and constexpr madness, so I assume it's going to be leading to longer compilation times and hard to debug problems.

jstimpfle··on Ditch your mutex, you deserve better
Sorry, I don't find that convincing but rather construed. This still seems like "constructor" type code, so the final object is not ready and locking should not happen before all the protected fields are constructed.

There may be other situations where you have an object in a specific state that makes it effectively owned by a thread, which might make it possible to forgo locking it. These are all very ad-hoc situations, most of them would surely be very hard to model using the borrow checker, and avoiding a lock would most likely not be worth the hassle anyway.

Not sure how this can help me reduce complexity or improve performance of my software.

jstimpfle··on Ditch your mutex, you deserve better
Initialization is always special. A mutex can't protect that which doesn't exist yet. The right way to initialize your object would be to construct the message first, then construct the composite type that combines the message with a mutex. This doesn't require locking a mutex, even without any borrow checker or other cleverness.
jstimpfle··on Ditch your mutex, you deserve better
I use lots of locals but only to make my code very "local", i.e. fine-grained, editable and clear, using lots of temporary variable. No complicated expressions. That's all immutable data (after initialization). I rarely take the address of such data but make lots of copies. If I take its address, then as an immutable pointer, maybe not in the type system but at least in spirit.

I keep very little state on the stack -- mostly implicit stuff like mutex lock / mutex unlock. By "state" I mean object type things that get mutated or that need cleanup. I always have a "database schema" of my global state in mind. I define lots of explicit struct types instead of hiding state as locals in functions. I've found this approach of minimizing local state to be the right pattern because it enables composability. I'm now free to factor functionality into separate functions. I can much more freely change and improve control flow. With this approach it's quite rare that I produce bugs while refactoring.

So yes, I have lots of locals but I share basically none of them with other threads. Also, I avoid writing any code that blocks on other threads (other than maybe locking a mutex), so there's another reason why I would not intentionally share a local with another thread. Anything that will be shared with another thread should be allocated on the heap just for the reason that we want to avoid blocking on other threads.

In that sense, the borrow checker is a tool that would allow me to write code more easily that I never wanted written in the first place.

jstimpfle··on Ditch your mutex, you deserve better
Do you have an example? I don't program in Rust, but I imagine I'd rarely get into that situation. Either my variable is a local (in a function) in which case I can tell pretty easily whether I'm the only one accessing it. Or, the data is linked globally in a data structure and the only way to access it safely is by knowing exactly what you're doing and what the other threads are doing. How is Rust going to help here? I imagine it's only making the optimal thing harder to achieve.

I can see that there are some cases where you have heap-data that is only visible in the current thread, and the borrow checker might be able to see that. But I can imagine that there are at least as many cases where it would only get in the way and probably nudge me towards unnecessary ceremony, including run-time overhead.

jstimpfle··on Ditch your mutex, you deserve better
Given a data item of non-thread safe type (i.e. not Mutex<T> etc), the borrow checker checks that there's only ever one mutable reference to it. This doesn't solve concurrency as it prevents multiple threads from even having the ability to access that data.

Mutex is for where you have that ability, and ensures at runtime that accesses get serialized.

jstimpfle··on Giving C a superpower: custom header file (safe_c.h)
Productivity, portability, stability, mind-share, direct access to OS APIs... there's a lot of reasons to still use C.
jstimpfle··on Windhawk Windows classic theme mod for Windows 11
I agree there are many bad timer-waster animations. But animations can be a good thing. Take scrolling as an example. Pressing page-down on a text-page or in a text-editor, without animation, it takes me a lot of time and energy to find the place where I left off reading or editing before scrolling. A good animation can save a lot of time here. It's similar with other operations -- and I agree that those operations that we don't do that often tend to be the ones that profit more from animation, while the ones where we already know in advance what will happen can be made worse by animation. I think an animation should never slow down the user, they should not be blocking. An unfinished animation should not prevent the user from typing the next action.
jstimpfle··on C++ move semantics from scratch (2022)
> You can implement new types whose behavior is fully controlled by yourself.

Exactly, that's what I do. Now notice that there is not a good incremental way to go from this object model stuff, to a toolbox that lets you build many more routines out of reusable primitives (implemented mostly as POD structs and freestanding functions). It becomes worse if one has also made a lot of use of these silly access protectors, public/protected/private.

Which means that, when you start out with the C++ object model, and evolve the codebase, you are invariably going to paint yourself into a corner, and proceeding requires first undoing all the fluffy object stuff.

That's not some furious ranting disconnected from reality on my end. I've seen it many times in practice. Show me any large complex serious systems codebase that makes extensive use of that silly implicit stuff, where all the important function calls are basically out of your control, hidden in the "whitespace"... no, that's not the way to write a complex program. It doesn't work.

For balance, I concede that even large programs can use some self-contained container types, like std::vector, or even custom built ones. That's where the object model still works -- small, isolated stuff. However, as you scale, you tend to not include many isolated types. You program probably needs a good grip of memory allocation for example.

jstimpfle··on C++ move semantics from scratch (2022)
Chasing an object model may work for simple programs, but as the program gets more complex, it tends toward an exercise in futility.

The problem with the object concept is that there are many ways to partition all the data in a program into distinct objects. But types and object models need you to decide on one way of looking at the world, and it wants you to see the world like that throughout the codebase.

Taking copying as a specific example, there may well be more than one useful way of copying stuff. The best way to achieve what you need depends not only on functional factors but also non-functional factors (such as performance and concurrency for example). The more serious and involved a program gets, the more apparent it becomes that there is not one way to copy. And the harder and more arbitrary it becomes to single out one way of copying that should be "blessed" with a special syntax.

It's an arbitrary choice to single out a blessed way to copy, and that is also a choice against a lot of other useful ways to copy stuff. Having one blessed way makes a few things easier, but makes everything more complicated. There is now one way to copy that gets done quite implicitly using syntax, which draws on a mountain of special C++ semantics, and there are other ways that must be done using regular function calls. The end result is something that is more complex than just always making regular freestanding functions that do the thing that you want done.

(What copy routine will be chosen implicitly by syntax/semantics is mostly predicated on types, so you can always opt to add wrapper types to accomodate other ways of doing things. Now try wrapping the members inside the thing that you want copied, working against all the implicit semantics... that must be about the point where the boilerplate becomes unbearable, where C++ gets much more verbose and unreadable than just straightforward C).

The downside of always using function calls is that you can't profit from all the built-in implicit semantics stuff, but as said there is always a theshold of program complexity beyond which that stops being useful because there's much more stuff that the program needs to do that cannot profit from it.

jstimpfle··on C++ move semantics from scratch (2022)
It's getting ever more complicated and involved. I need both of my hands to count the number of times I've tried coming back to C++ and use its object model for good effect. C++ is fine for simple things, and if you're smart you can scale it a long way (since it contains C).

But when you try to use all these funny features you're enumerating there for something serious, it will invariably end up in an overcomplicated slow compiling morass. Even just trying to make the types click for inserting something into a non-trivial templatized hashmap becomes a tedious act, and the IDE cannot help anymore either.

(Last issue I had was with catching some exception just to ignore it. Turned out catch(std::bad_alloc) doesn't work, you need write catch (std::bad_alloc&).)

I prefer writing simple C-style C++ where I write whole subsystems from scratch, and I can be very clear about the semantics from the start, design in what matters, and leave out what doesn't. Adding all the built-in object semantics baggage is too much overhead.

jstimpfle··on C++ move semantics from scratch (2022)
"Deep copy" is quite hand-wavy. A useful program is not a collection of trees that you can just copy like that. Instead, typical program's data structures tend to have lots of references that let you go from anywhere to just about anywhere else. That's why "deep copy" is just a rough idea that may work for simple container types but not much else.
jstimpfle··on Palantir Thinks College Might Be a Waste. So It's Hiring High-School Grads
> Or are they top-down-control corporate-empowering centralizing technologies like webapps and bitcoin?

Not at all. Just general smaller projects to hopefully grow and be able to do better work in the future. Desktop GUI, rendering, networking, ...

jstimpfle··on Palantir Thinks College Might Be a Waste. So It's Hiring High-School Grads
Work has provided me with plenty opportunity to learn, at a foundational level even. It's also a question of how you approach work. Sometimes it's as easy as asking for a little bit of extra time or loudly considering a little side project as promising the insight that's required for the next task.

Work has grounded me in such a way that in hindsight I regret staying around uni as long as I did instead of entering the workforce as early as possible. While I was less than a brilliant student, I wouldn't go as far as saying that uni was a waste for me. There are some things that I might never have learned otherwise. But I would love to know the other paths I could have taken in my profession.

jstimpfle··on John Carmack on mutable variables
That doesn't affect what I said though. Register renaming and pipelining does not make mutation go away and doesn't allow you to work on multiple things "at once" through the same pointer.

It's still logically the same thing with these optimizations, obviously -- since they aren't supposed to change the logic.

jstimpfle··on Futurelock: A subtle risk in async Rust
I've certainly seen my share of buggy multi-threaded programming. On the other hand, that's nothing compared to all the buggy and bad code I've seen overall. And I don't think it's going to get better by telling people not to even try.

I'm very doubtful that multi-threading can be abstracted behind a library. Simple message passing can cover a lot of use cases but not everything by far. I've also seen the Javascript model work fine, but it's not real multi-threading (no parallelism). As to async frameworks, they too are restrictive and they come with a lot of complexity.

jstimpfle··on Palantir Thinks College Might Be a Waste. So It's Hiring High-School Grads
So in college you learn how the world works and in the real world you can't?

I can see how college can serve to lose one's arrogance, but why couldn't that happen while doing real work? (in general -- don't know about Palantir)

jstimpfle··on Palantir Thinks College Might Be a Waste. So It's Hiring High-School Grads
Is it college, or is the age where many young people visit college, that the start to mature?
jstimpfle··on Hard Rust requirements from May onward
You took "safe" to be "100% safe, never breaks" but you could have understood it as just "relatively safe" or "a lot safer". They even hinted at the cost that comes with more heavy-handed approaches, which by the way aren't perfectly safe either.

Btw. kindly look at the other issue that I overconfidently waved away as "probably a false positive"?

jstimpfle··on Hard Rust requirements from May onward
I find it surprising hearing statements like this from a developer of a tool for, well, C programmers mostly I guess? "Skill is all that is needed to prevent bugs and produce bug-free software" is a phrase I've never heard from an actual C programmer, but have heard plenty of times from detractors.

The C programmers I know are certainly not deluded or overconfident. They don't even think "their" language is a perfect one, or even a very good one. They just avoid black-and-white thinking. They take a practical approach about memory issues, seeing them more like any other kind of bug. It's a different aesthetics than you would maybe see from many Rust folks. They want to be productive and want to be in control and want to understand what their code does. In turn, they accept that in some cases, bugs (possibly memory bugs) creep in, some of which could go unnoticed for some time. They tend to not see that as a huge issue, at least in general, because an issue that has gone unnoticed (or didn't manifest) is often less of a problem than one that is immediately obvious. (In case of data corruption, it _can_ be a huge issue, and you have to add safeguards to prevent it, and have to be accepting some residual risk).

They understand that everything is a trade off and that with experience and practice, good architecture, good tooling etc. you can prevent many bugs early, and detect them early. They have tried many approaches to prevent bugs, including fancy languages and constructs, and have concluded that in many cases, perfect safety is not possible, in particular it's not possible without seriously hurting other requirements, such as productivity.

As to valgrind, I can say that it was a bit of a mixed bag for me. It did help me finding bugs a number of times, but I also had to configure it a bit because it was producing a lot of noise for some external libraries (such as libc). I don't really understand the underlying issues.

jstimpfle··on Futurelock: A subtle risk in async Rust
If you take programming serious, learn it.

With a library that encapsulates a low number of patterns (like message passing) you'll be very limited. If you never start learning about lower level multi-threading issues you'll never learn it. And it's not _that_ hard.

I'm not writing multi threaded every day (by far), but often enough that I can write useful things (using shared memory, atomics, mutexes, condition variables, etc). And I'm looking forward to learn more, better understand various issues, learn new patterns.

jstimpfle··on Futurelock: A subtle risk in async Rust
Are you saying GUIs in general don't need multithreading or just that you think you haven't needed it so far? Or that you use some high-level async framework that hides just the synchronisation bits (at the cost of async type complexity)?
jstimpfle··on Futurelock: A subtle risk in async Rust
People are messing up any number of things all the time. The more important question is, do you need to risk messing up in a particular situation? I.e. do you need multithreading? In many cases, for example HPC or GUI programming, the answer is yes, you need multithreading to avoid blocking and to get much higher performance.

With a little bit of experience and a bit of care, multithreading isn't _that_ hard. You just need to design for it. You can reduce the number of critical pieces.

jstimpfle··on John Carmack on mutable variables
Can you clarify?
jstimpfle··on Show HN: A fast, dependency-free traceroute implementation in pure C
I said "probably" because the other messages from clang-tidy were so low quality obvious false positives too, a waste of time. I've already given the remaining warning a look and the code seemed fine to me. I didn't follow the whole massive linter output. Did you?
jstimpfle··on Show HN: A fast, dependency-free traceroute implementation in pure C
> Were they false positives though?

I've looked at them -- not all of them but all that I've looked at were the same kind recommending memcpy_s instead of memcpy, and were ridiculously easily to classify as false positives. So yes.

> How come "which isn't that easily to provide in compiler explorer, " suddenly becomes me telling that was impossible?

You claimed it wasn't easy but it is easy. VERY easy. It's one flag to disprove your point. Be honest.

> The first concern is that some parts of the industry keep reaching to C when there are better alternatives, even the language authors have moved on to creating better languages.

Many still enjoy it, are productive, and are creating infrastructure for billions of people to use. Let's keep things in relation.

← PreviousPage 10 of 34Next →