HNHacker News
TopNewBestAskShowJobs

jstimpfle

4,224 karma · joined May 21, 2015

http://jstimpfle.de
submissionscomments
jstimpfle··on Show HN: A fast, dependency-free traceroute implementation in pure C
Instead of ranting and showing a huge warnings output to make a point fitting your agenda, you could have just disabled the false positives yourself (like I did, by the way) and you would have seen that that vastly reduces the warnings.

Oh, and to disprove your other claim, here is a link to the godbolt with added clang-tidy flag: https://godbolt.org/z/G31Ws8aa1 . This has the clang-tidy invocation changed to disable a single warning category : --checks='-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling' . Running with that, there remains only a single warning. Which is probably a false positive as well.

If there are real concerns about this code, show them. I'm not saying there can't be any. But it doesn't help your credibility if you continue arguing your claims with evidence that is easily disproved. I have nothing against tooling that actually improve the situation. Btw. that `lint` from almost 50 years ago that you're referencing is probably easily covered by `-Wall` or `-Wextra` alone. I was also mentioning valgrind.

Bottom line, you're vastly exaggerating the gravity of the memory bugs inflicted upon us by memory-unsafe languages, compared to other bugs which exist too. (Maybe I like the term memory-dangerous better).

jstimpfle··on John Carmack on mutable variables
Either you don't understand what you're talking about, or you've missed the word "strive" in the tweet.
jstimpfle··on John Carmack on mutable variables
Problem is that "copy" of an object is not well defined. It could be a "shallow" copy or a "deep" copy. The only models where "copy" is defined are simple "memory"/"value" models (like C) and immutable models (like Haskell).
jstimpfle··on Show HN: A fast, dependency-free traceroute implementation in pure C
I've looked at a couple of these complaints by clang-tidy, and as it unfortunately often is, all of them were false positives and overzealous nitpicks. All the complaints about memcpy and memset for example, clang-tidy could easily be improved to see that these are just fine and being dogmatic about using the "new and right way" to do things is not helpful.

In practice I've found -Wall with GCC to offer a good warning level and clang-tidy to not offer a lot of constructive feedback (besides it being very slow). For more ambitious projects, it's possible to fine-tune GCC warnings.

You can also, you know, just _use_ a program and see if there are any anomalies when running it. With some discipline to code structure, many problems get hit on the first run, and extensive testing can come a lot closer to static verification than you would think. For non-real-time constrained stuff there is also valgrind and other run-time instrumentation.

jstimpfle··on John Carmack on mutable variables
The immutable approach doesn't conflate the concepts of place, time, and abstract identity, like in-place mutation does.

In mutating models, typically abstract (mathematical / conceptual) objects are modeled as memory locations. Which means that object identity implies pointer identity. But that's a problem when different versions of the same object need to be maintained.

It's much easier when we represent object identity by something other than pointer identity, such as (string) names or 32-bit integer keys. Such representation allows us to materialize us different versions (or even the same version) of an object in multiple places, at the same time. This allows us to concurrently read or write different versions of the same abstract object. It's also an enabler for serialization/deserialization. Not requiring an object to be materialized in one particular place allows saving objects to disk or sending them around.

jstimpfle··on Roc Camera
Just taking a photo using a digital device doesn't imply uploading it either. I'm sure most jurisdictions clearly differentiate between these.
jstimpfle··on Roc Camera
There are people who can "take a picture of you" just by looking at you for a second. They have you memorized after that.

I believe the usual approach is that in general, if you're in a public space, you accept pictures may be taken of you. But it depends on the context. If you're a bystander in your city while tourists are fotographing places of interest for example, and you make it into the picture, then that will hardly be a problem in any practical legislation. Most legislations probably allow for pictures taken of you even without you being asked explicitly, as long as certain rights are not violated.

jstimpfle··on Forth: The programming language that writes itself
That's not right. C coders have no problem at all diving right into most other C codebases.
jstimpfle··on What Dynamic Typing Is For
The most important ingredient by far is competent people. Those people will then probably introduce some static analysis to find problems earlier and easier. But static analysis can never fix the wrong architecture and fix the wrong vision.

In the industries I've worked, it's not a huge problem if you have a bug. It's a problem if you can't iterate quickly, try out different approaches quickly, bring results quickly. A few bugs are acceptable as long as they can be fixed. I've even worked at a medical device startup for a bit and it wasn't different, other than at some point there need to happen some ISO compliance things. But the important thing is to get something off the ground in the first place.

jstimpfle··on What Dynamic Typing Is For
I agree 100÷ with this. I've had the same concerns for a long time but rarely see them expressed with such eloquence.
jstimpfle··on Upcoming Rust language features for kernel development
Right you said move but I was thinking of swap for some reason.

I probably still wouldn't care, unless it's clear that moving to self is even required. Trying to break everything in a thousand pieces and generalizing and perfecting them individually is a lot of busywork.

jstimpfle··on Upcoming Rust language features for kernel development
> Complexity is fine. Sometimes we're working on complex problems without simple, straightforward, correct solutions.

Often the best way to proceed is to just solve a simpler problem :)

jstimpfle··on Upcoming Rust language features for kernel development
Why check this != other? I've seen this once before in a codebase and concluded it was unnecessary.

Asking as someone whose life became much easier after opting not do anything of the above and just write C in C++ ;-)

jstimpfle··on Zed is now available on Windows
It does not imply fractional glyph positioning though. By itself subpixel rendering simply exploits knowledge about the configuration of the subpixels (red, green, blue) of the pixels on a particular raster display. (horizontal, vertical, what order,... ?)
jstimpfle··on Zed is now available on Windows
You don't _need_ fractional offsets no?
jstimpfle··on Why is everything so scalable?
Something that does not scale to 10k users is likely so badly architected, it would be faster to iterate on it if it was more scalable hence better architected and more maintainable.
jstimpfle··on Love C, hate C: Web framework memory problems
You group things from the same context together, so you can free everything in a single call.
jstimpfle··on Love C, hate C: Web framework memory problems
No. Look up Arenas. In general group allocations to avoid making a mess.
jstimpfle··on Callbacks in C++ using template functors (1994)
What does codegen have to do with Qt or CMake (I don't use either)? I was only thinking about generating .[ch]{,pp} files with very simple code in them, and compiling them.

I'd recommend checking out e.g. https://github.com/EpicGamesExt/raddebugger . They are doing a lot of codegen. The codebase is very self-sufficient. Currently works only on Windows x64 but coming to Linux soon it sounds like. (And the reason why not yet isn't codegen)

About embedding non-code resources, yes there are platform dependent solutions to file embedding that you'd have to support separately, but you can also ship an executable with separate asset files. With bigger assets that's probably the way to go. You can of course also opt to encode a file as C code (char data) but it's not very efficient.

jstimpfle··on Callbacks in C++ using template functors (1994)
What is the issue with codegen for multiple platforms? Why not just emit regular C or C++ code (and data) -- preferably well abstracted from the underlying OS?

Or is your concern about integration into the build system of any prebuild-events or similar logic that need to be run in addition to compiling and linking? This integration may require separate efforts for each platform. But such support is very useful for a bunch of different things in most non-trivial projects, such that it's better to pay for that effort early, rather than develop workarounds to avoid it in my experience.

jstimpfle··on Callbacks in C++ using template functors (1994)
You would have added some codegen, should be possible to write a codegen framework from scratch in 2000 lines even.

Arguably the result would have been easier to read and maintain and not as slow to compile.

jstimpfle··on Arenas in Rust
Even if you just go for big-O, don't forget that a resizable array won't give you even amortized O(1) delete in many cases. This alone is likely prohibitive unless you can bound the elements in the container to a small number.

And if you're trying to trade away good big-O for better cache locality, don't forget that in many cases, you're dealing with stateful objects that need to be put into the list. That means you likely need to have a list or queue of pointers to these objects. And no matter how flat or cache-friendly the queue is, adding this indirection is similarly cache-unfriendly whenever you have to actually access the state inside the container.

jstimpfle··on Arenas in Rust
So, as an example, you'd be happily spending an extra allocation and extra pointers of space for each item in a list, even when that item type itself is only a couple of bytes, and you potentially need many millions of that type? Just so your design is not "from the nineties"?

An extrusive list needs at least 1 more pointer (to the item, separate from the link node), and possibly an additional backpointer to the link node when you want to unlink that node. It also adds allocation overhead and cache misses.

Intrusive lists are one of the few essential tools to achieve performance and low latency.

Or were you thinking of dynamically reallocating vectors? They are not an alternative, they are almost completely unusable in hardcore systems programming. Reallocating destroys pointer stability and adds latency, both very bad for concurrency.

jstimpfle··on Wild performance tricks
From a skim, my impression was that there was a specific stream processing problem discussed but I probably have gotten that wrong, or a comment was edited. If I understand better now, in the OP, Rust streaming features are being used here to achieve essentially a typecast, and the streaming transformations (looking at each individual element) are supposed to be optimized away. And I restate that this is essentially solving a language-inflicted problem, not some interesting performance problem.

For sure, linking "graphs" generally have random connections so obviously I'm not saying that linking in general is a trivial stream processing problem.

Thinking about it though, linking graphs are _almost_ free of cyclic dependencies in practice (even when looking at entire chunks of code or whole object files as units), so there are some basic tricks we can use so we don't need to load all compilation units into RAM as I think you claimed we need to. I don't think it's necessary to load all the data in RAM.

The way I understand it, linking is essentially concatenating object files (or rather the sections contained in them) into a final executable. But while doing so, relocations must be applied, so object code must not be written before all symbols referenced by that code are known. This can be done in a granular fashion, based on fixed-size blocks. You collect all relocations in per-block lists (unsorted, think std::vector but it can be optimized). When a symbol or code location becomes known (i.e. looking at the library that has the required symbol), that information gets appended to the list. When a list is full (i.e. all relocations known) that block can be committed to the file while applying all fixups. Applying fixups is still random writes but only to fixed size blocks.

That way the problem is almost reduced to streaming fixed size blocks in practice. Typically, all symbols that some object file depends on are typically resolved by the object files that were already added before. So most new chunks that we're looking at can immediately be fixed up and be streamed to the output file. (In fact some linkers are kind of strict about the order of objects that you pass on the command line). What does grow more-or-less linearly though is the set of accumulated symbols (e.g., name + offset) as we add more and more objects files to the output executable.

I don't know a lot about LTO but it seems just like an extension of the same problem. Balancing memory consumption and link times with output performance is partly the user's job. Most projects should probably just disable LTO even for release builds. But making LTO fast in a linker is probably all about using the right approach, doing things in the right order, grouping work to avoid reads and writes (both RAM and disk).

jstimpfle··on Wild performance tricks
I'm good, thanks.
jstimpfle··on Wild performance tricks
No, without more context I don't immediately see how it would fall flat. If you're dealing with larger data, like Gigabytes of data, it might be better to use chunks of 1 MB or sth like that. My point is that stream processing is probably best done in chunks. Otherwise, you have to to fit all the transformed data in RAM at once -- even when it's ephemeral. Wasting Gigabytes without need isn't great.
jstimpfle··on Wild performance tricks
No, C doesn't actually handle the _complexity_ of writing assembly. It abstracts and automates a lot of the repetitive work of doing register allocation etc -- sure. But these are very local issues -- I think it's fair to say that the complexity of a C program isn't really much lower than the equivalent program hand-coded in assembler.

I'm not sure that LLVM would be the first consideration for complex, flexible, efficient? It's quite certainly not fast, in particular linking isn't. I'm not sure about Chromium, it would be interesting to look at some of the more interesting components like V8, rendering engine, OS interfacing, the multimedia stack... and how they're actually written. I'd suspect the code isn't slinging shared_ptr's and unique_ptrs and lambdas and is keeping use of templates minimal.

I would have thought of the Linux kernel first and foremost. It's a truly massive architecture, built by a huge number of developers in a distributed fashion, with many intricate and highly optimized parts, impressive concurrency, scaling from very small machines to the biggest machines on the planet.

jstimpfle··on Wild performance tricks
That's my point; I think after a while you instinctly repeat a command with sudo tacked on (see XKCD), and I wonder if I'm any safer from myself like that?

I'm doubtful that those boundaries that you mention really work so great. I imagine that in practice you can easily trigger faulty behaviours in unsafe code from within safe code. Practical type systems are barely powerful enough to let you inject a proof of valid-state into the unsafe-call. Making a contract at the safe/unsafe boundary statically enforceable (I'm not doubting people do manage to do it in practice but...) probably requires a mountain of unessential complexity and/or runtime checks and less than optimal algorithms & data structures.

jstimpfle··on Wild performance tricks
I've considered writing one. Why do you think what I'm describing here only applies to I/O (like syscalls)? And, more abstractly speaking -- isn't everything an I/O (like input/output) problem?
jstimpfle··on Wild performance tricks
I'm not even sure what it means for a language to "have" abstractions. Abstractions are created by competent software engineers, according to the requirements. A language can have features that make creating certain kinds of abstractions easier -- for example type-abstractions. I've stopped thinking that type abstractions are all that important. Somehow creating those always leads to decision paralysis and scope creep, and using those always leads to layers of bloat and less than straightforward programs.
← PreviousPage 11 of 34Next →