I basically agree with you, but I’ll point out that this is a “weak man” argument, in that he’s criticizing an actually existing set of guidelines. They don’t do justice to the general viewpoint he’s arguing against, but they’re not a made up position–someone really was offering code that bad!
It’s a remarkable thing that a lot of “clean code” examples are just terrible (I don’t recognize these, but Bob Martin’s are quite awful). I think a significant problem is working with toy examples–Square and Circle and all that crap (there’s a decent chance that neither the readers nor the authors have ever implemented production code with a circle class).
Conversely, I’d like to see Muratori’s take on an average LOB application. In practice, you’ll get more than enough mileage out of avoiding n+1 queries in your DB that you might never need to worry about the CPU overhead of your clean code.
Edit: clarifying an acronym. LOB = Line of Business–an internal tool, a ticket tracker, an accounting app, etc. something that supports a company doing work, but is typically not at huge scale.
Conversely, I’d like to see Muratori’s take on an average LOB application. In practice, you’ll get more than enough mileage out of avoiding n+1 queries in your DB that you might never need to worry about the CPU overhead of your clean code.
I have not enough upvotes to give.
It’s really not that performance isn’t important - it really really is! It’s just that, what goes into optimizing a SQL query is totally different than what goes into optimizing math calculations, and games have totally different execution / performance profiles than business apps. That context really matters.
But you don’t understand! These guys like Muratori and Acton are rockstar guru ninja wizard god-tier 100000000x programmers who will walk into the room and micro-optimize the memory layout of every object in your .NET web app.
And if you dare to point out that different domains of programming have different performance techniques and tradeoffs, well, they’ll just laugh and tell you that you’re the reason software is slow.
Just don’t mention that the games industry actually has a really terrible track record of shipping correct code that stays within the performance bounds of average end-user hardware. When you can just require the user to buy the latest top-of-the-line video card three times a year, performance is easy!
(this is only half joking – the “you’re the reason” comment by Acton gets approvingly cited in lots of these threads, for example)
When you can just require the user to buy the latest top-of-the-line video card three times a year, performance is easy!
You cite Acton who, working at Insomniac, primarily focused on console games–you know, where you have a fixed hardware budget for a long time and can’t at all expect upgrades every few months. So, bad example mate.
And if you dare to point out that different domains of programming have different performance techniques and tradeoffs, well, they’ll just laugh and tell you that you’re the reason software is slow.
Acton’s been pretty upfront about the importance of knowing one’s domain, tools, and techniques. The “typical C++ bullshit” he’s usually on about is caused by developers assuming that the compiler will do all their thinking for them and cover up for a lack of basic reasoning about the work at hand.
primarily focused on console games
And yet is held up again and again as an example of how the games industry is absolutely laser-focused on performance. Which is a load of BS. The games industry is just as happy as any other field of programming to consume all available cycles/memory, and to keep demanding more. And the never-ending console upgrade treadmill is slower than the never-ending PC video card upgrade treadmill, but it exists nonetheless and is driven by the needs of game developers for ever-more-powerful hardware to consume.
Acton’s been pretty upfront about the importance of knowing one’s domain, tools, and techniques.
The “you’re the reason why” dunk from Acton was, as I understand it, a reply to someone who dared suggest to him that other domains of programming might not work the same way his does.
The “you’re the reason why” dunk from Acton was, as I understand it, a reply to someone who dared suggest to him that other domains of programming might not work the same way his does.
He was objecting to someone asserting that Acton was working in a very narrow, specialised field. That in most cases performance is not important. This is a very widespread belief, and a very false one. In reality performance is not a niche concern. When I type a character, I’d better not notice any delay. When I click on something I’d like a reaction before 100ms. When I drag something I want my smooth 60 FPS or more. When I boot a program I’d better take less than a second to boot unless it has a very good reason to make me wait.
Acton was unkind. I feel for the poor guy. But the attitude of the audience member, when checked against reality, is utterly ridiculous, and deserves to be publicly ridiculed. People need to laugh at the idea that performance does not matter most of the time. And people who actually subscribed to this ridiculous beliefs should be allowed to pretend they didn’t really believe it.
Sufficient performance rarely requires actual optimisations, but it always matters.
He was objecting to someone asserting that Acton was working in a very narrow, specialised field. That in most cases performance is not important.
Well. This:
As I was listening to your talk, I realise that you are working in a different kind of environment than I work in. You are working in a very time constrained, hardware constrained environment.
[elided rant] Our company’s primary constraint are engineering resources. We don’t worry so much about the time, because it’s all user interface stuff. We worry about how long it takes a programmer to develop a piece of code in a given length of time, so we don’t care about that stuff. If we can write a map in one sentence and get the value out, we don’t care really how long it takes, so long —
Then Mike Acton interrupted with
Okay, great, you don’t care how long it takes. Great. But people who don’t care how long it takes is also the reason why I have to wait 30 seconds for Word to boot for instance.
Is how you chose to describe it last time we went round and round about this. And, yeah, my takeaway from this was to form a negative opinion of Acton.
So I’ll just paste my own conclusion:
Meanwhile: I bet Mike Acton doesn’t work exclusively in hand-rolled assembly. I bet he probably uses languages that are higher-level than that. I bet he probably uses tools that automate things in ways that aren’t the best possible way he could have come up with manually. And in so doing he’s trading off some performance for some programmer convenience. Can I then retort to Mike Acton that people like him are the reason some app he didn’t even work on is slow? Because when we reflect on this, even the context of the quote becomes fundamentally dishonest – we all know that he accepts some kind of performance-versus-developer-convenience tradeoffs somewhere. Maybe not the same ones accepted by the person he was trying to dunk on, but I guarantee there are some. But haggling over which tradeoffs are acceptable doesn’t let him claim the moral high ground, so he has to dunk on the idea of the tradeoff itself.
So again: “People like you are the reason that it takes 30 seconds to open Word” is not a useful statement. It’s intended solely as a conversation-ending bludgeon to make the speaker look good and the interlocutor look wrong. There’s no nuance in it. There’s no acknowledgment of complexity or tradeoffs or real-world use cases. Ironically, in that sense it almost certainly violates some of his own “expectations” for “professionals”. And as I’ve explained, it’s inherently dishonest!
And in so doing he’s trading off some performance for some programmer convenience. Can I then retort to Mike Acton that people like him are the reason some app he didn’t even work on is slow?
According to his talks, he 100% uses higher-level languages where it makes sense–during the programming. The actual end-user code is kept as fast as he can manage. It’s not a sin to use, say, emacs instead of ed provided the inefficiency doesn’t impact the end-user.
the interlocutor look wrong.
The interlocutor was wrong, though. They said:
If we can write a map in one sentence and get the value out, we don’t care really how long it takes,
That’s wrong, right? At the very least, it’s incredibly selfish–“We don’t care how much user time and compute cycles we burn at runtime if it makes our job at development time easier”. That’s not a tenable position. If he’d qualified it as “in some percent of cases, it’s okay to have thus-and-such less performance at runtime due to development speed concerns”, that’d be one thing…but he made a blanket statement and got justifiably shut down.
(Also: you never did answer, in the other thread, if you have direct first-hand experience of this stuff beyond Python/Django. If you don’t, it makes your repeated ranting somewhat less credible–at least in my humble opinion. You might lack experience with environments where there really is a difference between costs incurred during development time vs compile time vs runtime.)
It’s not a sin to use, say, emacs instead of ed provided the inefficiency doesn’t impact the end-user.
And yet when someone came forward saying they had a case where inefficiency didn’t seem to be impacting the end-user, he didn’t sagely nod and agree that this can be the case. Instead he mean-spiritedly dunked on the person.
Also: you never did answer, in the other thread, if you have direct first-hand experience of this stuff beyond Python/Django.
Of what stuff? Have I written C? Yes. I don’t particularly like or enjoy it. Same with a variety of other languages; the non-Python language I’ve liked the most is C#.
Have I written things that weren’t web apps? Yes, though web apps are my main day-to-day focus at work.
If you don’t, it makes your repeated ranting somewhat less credible–at least in my humble opinion.
Your repeated attempts at insulting people as a way to avoid engaging with their arguments make your comments far less credible, and I neither feel nor need any humility in telling you that.
Anyway, I’ve also pointed out, at length, multiple times, why the games industry is very far from being a paragon of responsible correctness-and-performance-focused development, which would make your entire attempt to derail the argument moot.
So. Do you have an actual argument worthy of the name? Or are you just going to keep up with the gatekeeping and half-insulting insinuations?
And yet when someone came forward saying they had a case where inefficiency didn’t seem to be impacting the end-user, he didn’t sagely nod and agree that this can be the case.
Check your transcript, ypu’re putting words into the dude’s mouth. The dude said, specifically, that they didn’t care at all about UI performance. Not that the user didn’t care–that the developers didn’t care. He was right to be slapped down for that selfishness.
Your repeated attempts at insulting people as a way to avoid engaging with their arguments make your comments far less credible, and I neither feel nor need any humility in telling you that.
As I said in the other thread, it has some bearing here–you’ve answered the question posed, so I can discuss accordingly. If you get your feathers ruffled at somebody wondering about the perspective of somebody who only lists python and webshit on their profile in a discussion on optimizing compiled languages and game development, well, sorry I guess?
I’ve also pointed out, at length, multiple times, why the games industry is very far from being a paragon of responsible correctness-and-performance-focused development
I think you’ve added on the “correctness” bit there, and correctness is something that has a large enough wiggle room (do we mean mathematically sound? do we mean provable? do we mean no visible defects? do we mean no major defects?) that I don’t think your criticism is super helpful.
The performance bit, as you’ve gone into elsewhere, I understand as “Some game developers misuse tools like Unity to make slow and shitty games, so clearly no game developer cares about performance”. I don’t think that’s a fair argument, and I especially think it’s incorrect with regards specifically here to Acton who has built an entire career on caring deeply about it.
Also, you didn’t really answer my observation about development time inefficiency versus runtime inefficiency and how it applied in Acton’s case.
Anyways, this subthread has gotten long and I don’t think we’re much farther in anything. Happy to continue discussion over DMs.
If you get your feathers ruffled at somebody wondering about the perspective of somebody who only lists python and webshit on their profile
You’re the one who can’t even refer to web development without needing to use an insulting term for it.
a discussion on optimizing compiled languages and game development
Well, no. The claim generally being made here is that people like Acton uniquely “care about performance”, that they are representative of game developers in general, and thus that game developers “care about performance” while people working in other fields of programming do not. And thus it is justified for people like Acton to dunk on people who explain that the tradeoffs are different in their field (and yes, that’s what the person was trying to do – the only way to get to the extreme “doesn’t care” readings is to completely abandon the principle of charity in pursuit of building up a straw man).
Which is a laughable claim to make, as I’ve pointed out multiple times. Game dev has a long history of lackluster results, and of relying on the hardware upgrade treadmill to cover for performance issues – just ship it and tell ’em to upgrade to the latest console or the latest video card!
And it is true that the performance strategies are different in different fields, but Acton is the one who did a puffed-up “everyone who doesn’t do things my way should be fired” talk – see the prior thread I linked for details – which tried to impose his field’s performance strategies as universals.
Like I said last time around, if he walked into a meeting of one of my teams and started asking questions about memory layout of data structures, he wouldn’t be laughed out of the room (because that’s not how my teams do things), but he would be quietly pulled aside and asked to get up to speed on the domain at hand. And I’d expect exactly the same if I walked into a meeting of a game dev team and arrogantly demanding they list out all the SQL queries they perform and check whether they’re using indexes properly, or else be fired for incompetence.
[quote] Is how you chose to describe it last time we went round and round about this.
Ah, thanks for the link. My only choice there was the elision, it was otherwise an exact transcript.
People like you are the reason that it takes 30 seconds to open Word” is […] intended solely as a conversation-ending bludgeon to make the speaker look good and the interlocutor look wrong.
I agree partially. It is a conversation-ending bludgeon, but it was not aimed directly at the interlocutor. Acton’s exact words were “But people who don’t care how long it takes is also the reason why I have to wait 30 seconds for Word to boot for instance.” He was ridiculing the mindset, not the interlocutor. Which by the way was less unkind than I remembered.
I know it rubs you the wrong way. I know it rubs many people the wrong way. I agree that by default we should be nice to each other. I do not however extend such niceties to beliefs and mindsets. And to be honest the mindset that by default performance does not matter only deserves conversation-ending bludgeons: performance matters and that’s the end of it.
And to be honest the mindset that by default performance does not matter
Except it’s a stretch to say that the “mindset” is “performance does not matter”. What the person was very clearly trying to say was that they work in a field where the tradeoffs around performance are different. Obviously if their software took, say, a week just to launch its GUI they’d go and fix that because it would have reached the point of being unacceptable for their case.
But you, and likely Acton, took this as an opportunity to instead knock down a straw man and pat yourselves on the back and collect kudos and upvotes for it. This is fundamentally dishonest and bad behavior, and I think you would not at all enjoy living in a world where everyone else behaved this way toward you, which is a sure sign you should stop behaving this way toward everyone else.
You’re entitled to your interpretation.
Other Lobsters are entitled to the original context, so here it is: Mike Acton was giving a keynote at CppCon. After the keynote it was time for questions, and one audience member came with this intervention. Here’s the full transcript:
As I was listening to your talk, I realise that you are working in a different kind of environment than I work in. You are working in a very time constrained, hardware constrained environment.
As we all know the hardware, when you specify the hardware as a platform, that’s true because that’s the API the hardware exposes, to a developer that is working inside the chip, on the embedded code, that’s the level which is the platform. Ideally I suspect that the level the platform should be the level in which you can abstract your problem, to the maximum efficiency. So I have a library for example, that allows me to write code for both Linux and Windows. I have no problem with that.
Our company’s primary constraint is engineering resources. We don’t worry so much about the time, because it’s all user interface stuff. We worry about how long it takes a programmer to develop a piece of code in a given length of time, so we don’t care about all that stuff. If we can write a map in one sentence and get the value out, we don’t care really how long it takes, as long as it’s… you know big O —
Acton then interrupts:
Okay, great, you don’t care how long it takes. Great. But people who don’t care how long it takes is also the reason why I have to wait 30 seconds for Word to boot for instance.
But, you know, whatever, that’s your constrain, I mean, I don’t feel that’s orthogonal to what we do. We worry about our resources. We have to get shit out, we have to build this stuff on time, we worry about that. What we focus on though is the most valuable thing that we can do, we focus on understanding the actual constraints of the problem, working to actually spend the time to make it valuable, ultimately for our players, or for whoever your user is. That’s where we want to bring the value.
And performance matters a lot to us, but I would say, any context, for me personally if I were to be dropped into the “business software development model” I don’t think my mindset would change very much, because performance matters to users as well, in whatever they’re doing.
That’s my view.
Thanks for including the full transcript. I didn’t realize the questioner was a “Not a question, just a comment” guy and don’t think we should judge Mike Acton based on how he responded. Comment guys suck.
This is a guy who thought “Everyone Watching This Is Fired” was a great title for a talk. And not just that, a talk that not only insisted everyone do things his way, but insisted that his way is the exclusive definition of “professional”.
So I’ll judge him all day long.
Thanks for quoting extensively.
I worked with GUIs in a past life, and debugged performance problems. I’ve never seen a 30-second wait that was primarily due to user interface code, or even a five-second wait.
One can alwys discuss where in the call stack the root of a problem is. The first many-second delay I saw (“people learn not to click that button when there are many patients in the database”) was due to a database table being sorted as part of processing a button click. IMO sorting a table in UI code is wrong, but the performance problem happened because the sorting function was somewhere between O(n²) and O(n³): A fantastic, marvellous, awesome sorting algorithm. I place >90% of the blame in the sorting algorithm and <10% in the UI code that called the function that called the sorting marvel.
In my experience, delays that are primarily due to UI code are small. If the root reason is slow UI code, then the symptoms are things like content is updated slowly enough to see jerking. Annoying, sure, but I agree that quick development cycles can justify that kind of problem.
The many-second delays that Acton ascribes to slow UI code are more typically due to UI code calling some sparsely documented function that turns out to have problems related to largish data sets. Another example I remember is a function that did O(something) RPCs and didn’t mention that in its documentation. Fine on the developers’ workstations, not so fine for production users with a large data set several ms away.
I’m sure Mike Acton conflated the two by mistake. They’re not the same though.
I realise that neither the audience member nor Mike Acton mentioned GUI specifically. Acton merely observes that because some people don’t care enough about performance, some programs take an unacceptably long time to boot. I’m not sure Acton conflated anything there.
I might have.
As for the typical causes of slowdowns, I think I largely agree with you. It’s probably not the GUI code specifically: Even Electron can offer decent speed for simple applications on modern computers. It’s just so… huge.
I realise that neither the audience member nor Mike Acton mentioned GUI specifically.
Opps, the commenter did briefly mention “it’s all user interface stuff”. I can’t remember my own transcript…
Acton is still taking the least charitable possible interpretation and dunking on that. And he’s still doing it dishonestly – as I have said, multiple times now, it’s an absolute certainty that he also trades off performance for developer convenience sometimes. And if I were to be as uncharitable to him as he is to everyone else I could just launch the same dunk at him, dismiss him as someone who doesn’t care about performance, say he should be fired, etc. etc.
And the never-ending console upgrade treadmill is slower than the never-ending PC video card upgrade treadmill, but it exists nonetheless and is driven by the needs of game developers for ever-more-powerful hardware to consume.
If you compare the graphics of newer games to older ones, generally the newer ones have much more detail. So this improved hardware gets put to good use. I’m sure there are plenty of games that waste resources, too, but generally speaking I’d say things really are improving over time.
I haven’t touched a new game in 10, 15 years or so, but I hear games like The Last of Us win critical acclaim, probably due to their immersiveness which is definitely (also) due to the high quality graphics. But that wasn’t really the point - the point was that the hardware specs are put to good use.
I dunno, I feel like games from ten years ago had graphics that were just fine already. And really even 15 years ago or more, probably. We’re mostly getting small evolutionary steps — slightly more detail, slightly better hair/water – at the cost of having to drop at least hundreds and sometimes thousands of dollars on new hardware every couple of years.
If I were the Mike Acton type, I could say a lot of really harsh judgmental things about this, and about the programmers who enable it.
These guys like Muratori and Acton are rockstar guru ninja wizard god-tier 100000000x programmers who will walk into the room and micro-optimize the memory layout of every object in your .NET web app.
Muratori is on the record saying, repeatedly, that he’s not a great optimiser. In his Refterm lecture he mentions that he rarely optimises his code (about once a year).
It’s just that, what goes into optimizing a SQL query is totally different than what goes into optimizing math calculations, and games have totally different execution / performance profiles than business apps.
The specifics differ (e.g., avoiding L2 cache misses vs. avoiding the N+1 problem), but the attitude of the programmers writing those programs matters also. If their view is one where clean code matters, beautiful abstractions are important, solving the general problem instead of the concrete instance is the goal, memory allocations are free, etc. they will often miss a lot of opportunities for better performance.
I will also add that code that performs well is not antithetical to code that is easy to read and modify. Fully optimized code might be, but we can reach very reasonable performance with simple code.
I think it’s subsequently been deleted, but I remember a tweet that showed a graph, clarity on the y axis, performance on the x axis.
ave
I would say that is most cases it still fundamentally boils down to how data is laid out, accessed, and processed. Whether an SQL query or later processing of data loaded from an SQL query, focus on data layout and accesses is the core of a lot of performance.
I’m normally a fan of Casey’s work, but yeah this one’s not great. The clean code vs fast code battle doesn’t really help the article, and since he “only” demonstrates a 15x speedup it doesn’t actually address the problem we face today.
If all the software I used was 15x slower than optimal, almost everything I did on my computer would complete instantly, but in reality I would guess the majority of the software I use ranges from 1000x to tens of millions of times too slow. Here are some examples I got annoyed by in the last few days:
(These measurements were taken by counting seconds in my head because even with 50% error bars they’re still ridiculous, on a laptop whose CPU/RAM/SSD benchmark within 10x of the best desktop hardware available today)
That sound ridiculously slow, and to me mostly related to macOS. I’m on a decent laptop, using Ubuntu and do no experience such crazy delays.
Which “fast” terminal emulator are you using? I’m using alacritty’s latest version, and can’t notice any delay in either startup or typing latency (and I’m usually quite sensitive to that). Even gnome-terminal starts reasonably fast, and it’s not known for being a speed beast.
For the lldb test, I get <1s both cold and warm, no noticeable difference between the two.
A nonzero amount of it is on macOS yes, e.g. the first time you run lldb/git/make/etc after a reboot it has to figure out which of the one version of lldb/git/make/etc you have installed it should use, which takes many seconds. But it is at least capable of warm booting non-trivial GUI apps in low-mid hundreds of ms so we can’t put all the blame there.
Which “fast” terminal emulator are you using?
It’s alacritty. I’ve tried it on all 3 major OS with mixed results. On Windows startup perf is good, typing latency is good but everything is good at 280Hz, and it’s so buggy as to be near unusable (which makes it the second best Windows terminal emulator out of all the ones I’ve tried). On macOS startup perf is bad but typing latency is fine. On Linux startup perf is fine but typing latency makes it unusable.
Nah goto actually sucks and this article is missing both the main downside of using it like this, and the best alternative.
You can’t jump over variable declarations with goto, which in practice forces you to write them all at the top of your function, which makes it harder to see where your variables actually get used and for how long and leads to accidental reuse etc.
The best alternative is to compile as C++, use RAII for the common/easy cases, and roll a defer keyword for everything else:
#define CONCAT_HELPER( a, b ) a##b
#define CONCAT( a, b ) CONCAT_HELPER( a, b )
#define COUNTER_NAME( x ) CONCAT( x, __COUNTER__ )
template< typename F >
struct ScopeExit {
ScopeExit( F f_ ) : f( f_ ) { }
~ScopeExit() { f(); }
F f;
};
struct DeferHelper {
template< typename F >
ScopeExit< F > operator+( F f ) { return f; }
};
#define defer [[maybe_unused]] const auto & COUNTER_NAME( DEFER_ ) = DeferHelper() + [&]()
void f() {
void * p = malloc( 1 );
defer { free( p ); };
memset( p, 0, 1 );
}
It’s easy to dislike goto if changing to another language is an option. But when strictly C is required, goto is very useful for error handling in complex functions. See driver probe functions in the Linux kernel for example.
Not sure about other dialects, it GNU C has a cleanup attribute that lets you register a function to be called when a variable goes out of scope. This makes it possible to write RAII-style code with lexically scoped cleanups. The compiler is really good at inlining them and you end up with the same code generated as the goto version, but much harder to get wrong.
Okay, but this is about C, not C++. What do you propose as the alternative to goto for C?
Compiling C with a C++ compiler actually really sucks. C++ is basically C89, with some small improvements, but without implicit casting of void*. You don’t get compound literals, designated initializers, or any of the other many improvements since C89 which C++ hasn’t incorporated yet. If you’re gonna write C++, write C++.
You can’t jump over variable declarations with goto, which in practice forces you to write them all at the top of your function, which makes it harder to see where your variables actually get used and for how long and leads to accidental reuse etc.
I would argue if you need to define your variables not at the start of a scope, your scope is to big. Having variable declarations in the middle of the code makes the function less readable. So yes then you can’t just jump in an other scope, but this wouldn’t go with the argument the post is making. Because this wouldn’t be readable.
Yes const is then a problem, but this problem is independent of goto.
The best alternative is to compile as C++
Best advice just use an other language. Hey in rust this is build in, or in java the GC will do the cleanup so just use one of these languages.
By the way: The “Error-Checking Convenience Macros for C” of “Advanced UNIX Programming” are more or less the same what the author is suggesting and are a bit better to read.
Having variable declarations in the middle of the code makes the function less readable.
The problem with moving variable declarations to the start is that you move declaration away from initialisation. This make it harder to read to code because you need to look at three lines of code (declaration, initialisation, and use) to understand a use. In C, this is even worse because you can refer to a variable between the declaration and initialisation and you get an unspecified value.
Why does it make the function less readable to have variable declaration in the middle? Maybe it’s me who tends to use type inference in other languages but I have honestly never encountered a situation where I lost readability because I declared on first assignment.
It’s not possible in standard C, but very little that’s actually useful is. C compilers that support GNU C (gcc, clang, icc, xlc, and so on) provide __attribute__((cleanup))
, which can do the same think. I’ve used it a lot in C codebases for things like acquiring a lock for a lexical scope or having lexical lifetimes on heap allocations. Probably the most fun use involved creating a linked list of on-stack structures across threads that let a thread see if a blocked thread had started doing something before sleeping.
The other benefit of this attribute is that it is exception-safe if you compile with -fexceptions
. If your code is calling C++ or some other language that may throw exceptions, then throwing an exception will gracefully clean up the resources that you acquire, so you aren’t stuck leaking memory or locks in the presence of foreign exceptions.
Bing AI did a great job of creating media hype, but their product is no better than Google’s Bard.
This kinda feels like working as intended. Search engines have been near useless for 5+ years for entirely non-technical reasons. Bing could try to compete on result quality, which would be easy because Google is just SELECT FROM websites ORDER BY adsense_revenue DESC
nowadays. But apparently people are quite happy to read AI generated/human-that-couldn’t-pass-a-Turing-test generated content so they might as well only show ad fodder and buy users through ChatGPT hype.
The implementation of _Atomic by GCC was rushed with inadequate review. At some point, I’d like to define a new ABI that adds a lock word to any atomic types that can’t be represented with native atomic operations for the target (with futex-like interfaces in the kernel, this can be very efficient). Supporting this kind of use was part of the original design rationale.
I totally agree with the error-checking analogy. If you do ‘ordinary’ arithmetic on an atomic variable in C/C++, you get sequentially consistent behaviour. This is almost always correct, just slow. You need explicit operations only for performance (in C++, I’d love to have the default memory ordering in the type as well, but you can’t have everything). There are some exceptions. If you use operators like ++, you get atomic read-modify-write, which is probably what you meant, but if you do an assignment with a load of the same variable on the right (e.g. something like a = a + b instead of a += b) then you will get something racy. Having the atomic as part of the type makes it possible to add compiler warnings for this kind of pattern.
At some point, I’d like to define a new ABI that adds a lock word to any atomic types that can’t be represented with native atomic operations for the target (with futex-like interfaces in the kernel, this can be very efficient).
When would you ever want this behaviour?
The only times you use atomics are when you need specific performance guarantees, i.e. performance is correctness, and when you’re implementing the concurrency primitives yourself. In both cases adding mutexes around the ops breaks the code.
The C++ atomic stuff does behave like you described which is why you have to static_assert(std::atomic::<T>::is_always_lock_free)
(or fall back to the intrinsics pre C++17, which then breaks relacy/cdschecker…) in any code that uses it.
When would you ever want this behaviour?
Because the current implementation is painful.
The only times you use atomics are when you need specific performance guarantees, i.e. performance is correctness, and when you’re implementing the concurrency primitives yourself. In both cases adding mutexes around the ops breaks the code.
That is how they are implemented now. Anything larger than a certain size calls out to the atomics support library, which has a fixed-size table of mutexes and acquires the lock corresponding to the object being operated on. Because the table is fixed size, there’s a potential for contention between unrelated atomics.
The C++ atomic stuff does behave like you described which is why you have to static_assert(std::atomic::::is_always_lock_free) (or fall back to the intrinsics pre C++17, which then breaks relacy/cdschecker…) in any code that uses it.
Nope, C++‘s std::atomic<T>
is layout-compatible with _Atomic(T)
(this was not guaranteed by the standard, now it is thanks to Hans Boehm’s paper), it does not have an inline lock and will call back to exactly the same calls to the libatomics libraries.
Oh, and the intrinsics will also turn into the libcalls for anything where is_always_lock_free
would return false.
Ah so I misunderstood what you were pushing for.
Oh, and the intrinsics will also turn into the libcalls for anything where is_always_lock_free would return false.
Right, been a while since I worked with this stuff. Footguns all the way down :( EDIT: with the tiny silver lining that if you don’t -latomic they at least turn into link errors.
Footguns all the way down
Yup. The original WG21 proposal was not bad but the way WG14 ported it to C was a disaster. For example, a bunch of the functions in stdatomic.h
have volatile
qualifiers, when they should just need _Atomic
. GCC then made it worse by implicitly assuming that _Atomic(T)
and T
had the same representation.
This is a good read, but reminds me somewhat of DiWHY.
I’d be interested to quantify the impact of this on compile time. I’ll be sticking with macro enums!
It definitely hurts compile times, but the kind of code where it’s useful is often already quite heavily templated, so it’s hard to isolate the cost of the templates that made using magic_enum desirable, the cost of magic_enum, and the cost of the templates that were subsequently enabled by using magic_enum.
I’m all for bagging on C but this is a shitty blog post:
I wrote incorrect code and my program didn’t work, woe is me
This is exactly the manifestation of the machsimo attitude. The unwillingness to admit that programmers are not infallible, and they will sooner or later write incorrect programs. It’s not a point of pride to never make a mistake, it’s a naive dangerous overconfidence.
It has never been an issue whether C can be safe when it’s written perfectly. Every language is. Assembly, Malbolge and typing machine code directly with eyes blindfolded also make perfectly safe programs when the programmer writes perfectly correct code.
So the real issue is, when the programmer will inevitably write something incorrectly, whether the language will catch it, or cause doom.
Yeah it is. I should have expanded on that, which I will do now:
That section presents a theoretical “excellent C programmer”, then has them write something along the lines of int* x = malloc(4);
, as if that isn’t exactly the kind of thing you abstract away day 1 because it’s so dangerous to write by hand every time. It is a very real issue that C and its teaching materials do nothing to push you towards designs that make that example a non-issue (you can implicitly cast between pointer types in C out of the box!!), but it’s only a C issue, not an unsafe language issue.
It doesn’t make sense to act like there’s no useful design space between full footgun and full memory safety just because C exists, much in the same way it doesn’t make sense to say all memory safe languages are bad because Java exists, and no existing memory safe language is an unconditional upgrade over C/C++ so we can’t completely write them off yet.
Are we reading the same comment? I think your parent commenter would fully agree with the fact that programmers are fallible and writing in C makes it worse. I don’t sense that machismo attitude (which I would agree exists, and permeates things, but this is not that)
I completely agree with the original comment that this blog post is weak at best. And I disagree with your false dichotomy. We agree: the programmer will inevitably write something incorrect. It will happen. But there’s a much bigger spectrum between the language catching it and causing doom. (The fact this spectrum exists is why the blog post is weak)
Yes, it’s great when the language/compiler catches things. The more the better! But just because the compiler catches some things, many still go undetected. You can’t catch a logic error, is the point, and you can make a logic error in any language, so why rag on C if all people and languages are fallible?
To wit, in writing Rust of late, I have had more bugs come out of the dang ?
operator. It’s handy, yes, but it’s also an implicit return. There are plenty of a class of bugs where the programmer is trying to ensure some invariants but ends up silently no-oping the function instead of wildly complaining (especially true where Options are concerned). This is a logic bug. And, it’s fine, this isn’t an attack on Rust, just that, yes, programmers are fallible but, unlike the original blog post, writing in an air-quotes-safe language doesn’t help you here. (It can help you in other places, but if the blog post were better, it would expound on those wins, instead of suggesting a panacea)
There is both the case of “is the language defined well enough such that the compiler can catch it?”, but also “what happens if the compiler does not catch it?”
The problem in some languages is that if the compiler doesn’t catch it, then the code at runtime fails. In a language like C, the problem is that if the compiler doesn’t catch it, you may have just accepted executable code over the Interwebs that you are now diligently running on behalf of an attacker, or you gave away the keys to the kingdom, or ….
I’ve personally lived through a number of CVSS ~10 issues on commercial products that I was managing, and also open source libraries that my team developed. (My team included several full time OpenSSL committers when HeartBleed hit, which is the event that I remember most vividly, but it only had a CVSS of 5.0 🤦♂️. We used OpenSSL in literally hundreds of different commercial products.) Anything to avoid (and if not avoid, then to reduce the impact from) events like this is worth evaluating. That includes purposefully moving away from low level languages, when high level languages will do just as well.
Storing your Dropbox folder on an external drive is no longer supported by macOS.
As someone who has used Windows, macOS, Linux, and FreeBSD extensively as professional desktop OSs I still don’t understand the love so many hackers have for Apple kit.
Because not everyone needs the same features as you. I like that MacOS behaves close enough to a Linux shell, but with a quality GUI. I particularly like emacs bindings in all GUI text fields, and the ctrl/cmd key separation that makes terminals so much nicer to use. I like the out-of-the-box working drivers, without having to consult Wikis about which brands have working Linux drivers. I like the hardware, which is best in class by all metrics that matter to me, especially with Apple Silicon. I like the iPhone integration, because I use my iPhone a lot. I like AppleScript, and never bothered to learn AutoHotKey. I like that my MacBook wakes up from sleep before I can even see the display. I like the massive trackpad, which gives me plenty of space to move around the mouse. I like Apple Music and Apple Photos and Apple TV, which work flawlessly, and stream to my sound system running shairport-sync. I like Dash for docs, which has an okay-ish Linux port but definitely not the first class experience you get on MacOS. I like working gestures and consistent hotkeys, tightly controlled by Apple’s app design guidelines. I like that I can configure caps lock -> escape in the native keyboard settings, without remembering the X command or figuring out Wayland or installing some Windows thing that deeply penetrates my kernel.
I use Linux for servers. I have a Windows gaming PC that hangs on restart or shut down indefinitely until you cut power, and I don’t care enough to fix it because it technically still functions as a gaming PC. But for everything else I use MacOS, and I flat out refuse to do anything else.
As someone who ran various Linux distros as my main desktop OS for many years, I understand exactly why so many developers use Apple products: the quality of life improvement is staggeringly huge.
And to be honest, the longer I work as a programmer the more I find myself not caring about this stuff. Apple has demonstrated, in my opinion, pretty good judgment for what really matters and what’s an ignorable edge case, and for walking back when they make a mistake (like fixing the MBP keyboards and bringing back some of the removed ports).
You can still boot Linux or FreeBSD or whatever you want and spend your life customizing everything down to the tiniest detail. I don’t want to do that anymore, and Apple is a vendor which caters to my use case.
I am a macOS desktop user and I like this change. Sure, it comes with more limitations, but I think it is a large improvement over having companies like Dropbox and Microsoft (Onedrive) running code in kernel-land to support on-demand access.
That said, I use Maestral, the Dropbox client has become too bloated, shipping a browser engine, etc.
I don’t follow why the - good! - move to eliminate the need for kernel extensions necessitates the deprecation of external drives though.
I’m not a Dropbox user so I’ve never bothered to analyse how their kext worked. But based on my own development experience in this area, I assume it probably used the kauth kernel API (or perhaps the never-officially-public-in-the-first-place MAC framework API) to hook file accesses before they happened, download file contents in the background, then allow the file operation to proceed. I expect OneDrive and Dropbox got special permission to use those APIs for longer than the rest of us.
As I understand it, Apple’s issue with such APIs is twofold:
These aren’t unreasonable concerns, although the fact they’re still writing large amounts of kernel vulnerabilities panic bugs code themselves somewhat weakens their argument.
So far, they’ve been deprecating (and shortly after, hard-disabling) kernel APIs and replacing them with user-space based APIs which only implement a small subset of what’s possible with the kernel API. To an extent, that’s to be expected. Unrestricted kernel code is always going to be more powerful than a user space API. However, one gets the impression the kernel API deprecations happen at a faster pace than the user space replacements have time to mature for.
In this specific case, NSFileProvider
has a long and chequered history. Kauth was one of the very first kernel APIs Apple deprecated, back on macOS 10.15. It became entirely unavailable for us plebs on macOS 11, the very next major release. Kauth was never designed to be a virtual file system API, but rather an authorisation API: kexts could determine if a process should be allowed to perform certain actions, mainly file operations. This happened in the form of callback functions into the kext, in the kernel context of the thread of the user process performing the operation.
Unfortunately it wasn’t very good at being an authorisation system, as it was (a) not very granular and (b) leaving a few gaping holes because certain accesses simply didn’t trigger a kauth callback. (Many years ago, around the 10.7-10.9 days, I was hired to work on some security software that transparently spawned sandboxed micro VMs for opening potentially-suspect files, and denied access to such files to regular host processes; for this, we actually tried to use kauth for its intended purpose, but it just wasn’t a very well thought-out API. I don’t think any of Apple’s own software uses it, which really is all you need to know - all of that, sandboxing, AMFI (code signing entitlements), file quarantine, etc. uses the MAC framework, which we eventually ended up using too, although the Mac version of the product was eventually discontinued.)
Kauth also isn’t a good virtual file system API (lazily providing file content on access atop the regular file system) but it was the only public API that could be (ab)used for this purpose. So as long as the callback into the kext didn’t return, the user process did not make progress. During this time, the kext (or more commonly a helper process in user space) could do other things, such as filling the placeholder file with its true content, thus implementing a virtual file system. The vfs kernel API on the other hand, at least its publicly exported subset, is only suitable for implementing pure “classic” file systems atop block devices or network-like mounts. NSFileProvider
was around for a few years on iOS before macOS and used for the Usual File Cloud Suspects. Reports of problems with Google Drive or MS OneDrive on iOS continue to this day. With the 10.15 beta SDK, at the same as deprecating kauth, everyone was supposed to switch over to EndpointSecurity or NSFileProvider on macOS too. NSFileProvider dropped out of the public release of macOS 10.15 because it was so shoddy though. Apple still went ahead and disabled kauth based kexts on macOS 11 though. (EndpointSecurity was also not exactly a smooth transition: you have to ask Apple for special code signing entitlements to use the framework, and they basically ignored a load of developers who did apply for them. Some persevered and eventually got access to the entitlement after more than a year. I assume many just didn’t bother. I assume this is Apple’s idea of driving innovation on their platforms.)
Anyway, NSFileProvider did eventually ship on macOS too (in a slightly different form than during the 10.15 betas) but it works very differently than kauth did. It is an approximation of an actual virtual file system API. Because it originally came from iOS, where the UNIXy file system is not user-visible, it doesn’t really match the way power users use the file system on macOS: all of its “mount points” are squirrelled away somewhere in a hidden directory. At least back on the 10.15 betas it had massive performance problems. (Around the 10.14 timeframe I was hired to help out with a Mac port of VFSforGit, which originally used kauth. (successfully) With that API being deprecated, we investigated using NSFileProvider, but aside from the mount point location issue, it couldn’t get anywhere near the performance required for VFSforGit’s intended purpose: lazily cloning git repos with hundreds of thousands of files, unlike the kauth API. The Mac port of VFSforGit was subsequently cancelled, as there was no reasonable forward-looking API with which to implement it.)
So to come back to your point: these limitations aren’t in any way a technical necessity. Apple’s culture of how they build their platforms has become a very two-tier affair: Apple’s internal developers get the shiny high performance, powerful APIs. 3rd party developers get access to some afterthought bolt-on chicken feed that’s not been dogfooded and that you’re somehow supposed to plan and implement a product around during a 3-4 month beta phase, the first 1-2 months of which the only window in which you stand any sort of slim chance of getting huge problems with these APIs fixed. Even tech behemoths like Microsoft don’t seem to be able to influence public APIs much via Apple’s Developer Relations.
At least on the file system front, an improvement might be on the horizon. As of macOS 13, Apple has implemented some file systems (FAT, ExFAT and NTFS I think) in user space, via a new user space file system mechanism. That mechanism is not a public API at this time. Perhaps it one day will be. If it does, the questions will of course be whether
(The vfs subsystem could be used for implementing a virtual file system if you had access to some private APIs - indeed, macOS contains a union file system which is used in the recovery environment/OS installer - so there’s no reason Apple couldn’t export features for implementing a virtual file system to user space, even if they don’t do so in the current kernel vfs API.)
Part of that may also be the hardware, not the software.
My datapoint: I’ve never really liked macOS, and tried to upgrade away from a MacBook to a “PC” laptop (to run KDE on Linux) two years ago. But after some research, I concluded that - I still can’t believe I’m saying this - the M1 MacBook Air had the best value for money. All “PC” laptops at the same price are inferior in terms of both performance and battery life, and usually build quality too (but that’s somewhat subjective).
I believe the hardware situation is largely the same today, and will remain the same before “PC” laptops are able to move to ARM.
macOS itself is… tolerable. It has exactly one clear advantage over Linux desktop environments, which is that it has working fonts and HiDPI everywhere - you may think these are just niceties, but they are quite important for me as a Chinese speaker as Chinese on a pre-hiDPI screen is either ugly or entirely unreadable. My pet peeve is that the dock doesn’t allow you to easily switch between windows [1] but I fixed that with Contexts. There are more solutions today since 2 years ago.
[1] macOS’s Dock only switches between apps, so if you have multiple windows of the same app you have to click multiple times. It also shows all docked apps, so you have to carefully find open apps among them. I know there’s Expose, but dancing with the Trackpad to just switch to a window gets old really fast.
[macOS] has exactly one clear advantage over Linux desktop environments, which is that it has working fonts and HiDPI everywhere
Exactly one? I count a bunch, including (but not limited to) better power management, better support for external displays, better support for Bluetooth accessories, better and more user-friendly wifi/network setup… is your experience with Linux better in these areas?
My pet peeve is that the dock doesn’t allow you to easily switch between windows
Command+~ switches between windows of the active application.
better power management
This is one area that I’d concede macOS is better for most people, but not for me. I’m familiar enough with how to configure power management on Linux, and it offers much more options (sometimes depending on driver support). Mac does have good power management out of the box, but it requires third party tools to do what I consider basic functions like limiting the maximal charge.
The M1 MBA I have now has superior battery life but that comes from the hardware.
better support for external displays
I’ve not had issues with support for external monitors using KDE on my work laptop.
The MBA supports exactly one external display, and Apple removed font anti aliasing so I have to live with super big fonts in external displays. I know the Apple solution is to buy a more expensive laptop and a more expensive monitor, so it’s my problem.
better support for Bluetooth accessories
Bluetooth seems suck the same everywhere, I haven’t notice any difference on Mac - ages to connect, random dropping of inputs. Maybe it works better for Apple’s accessories which I don’t have any of, so it’s probably also my problem.
better and more user-friendly wifi/network setup
KDE’s wifi and network management is as intuitive. GNOME’s NetworkManager GUI used to suck, but even that has got better these days.
Command+~ switches between windows of the active application.
I know, but
I’ve used a Mac for 6 years as my personal laptop and have been using Linux on my work laptop.
Back then I would agree that macOS (still OS X then) was much nicer than any DE on Linux. But Linux DEs have caught up (I’ve mainly used KDE but even GNOME is decent today), while to an end user like me, all macOS seems to have done are (1) look more like iOS (nice in some cases, terrible in others) and (2) gets really buggy every few releases and returns to an acceptable level over the next few versions. I only chose to stay on a Mac because of hardware, their OS has lost its appeal to me except for font rendering and HiDPI.
better support for Bluetooth accessories
A bluetooth headset can be connected in two modes (or more), one is A2DP with high quality stereo audio, but no microphone channel, and the other one is the headset mode which has low quality audio but a microphone channel. On macOS the mode will be switched automatically whenever I join or leave a meeting, on Linux this was always a manual task that most of the time didn’t even work, e.g. because the headset was stuck in one of the modes. I can’t remember having a single issue with a bluetooth headset on macOS, but I can remember many hours of debugging pulseaudio or pipewire just to get some sound over bluetooth.
My pet peeve is that the dock doesn’t allow you to easily switch between windows
It sounds like you found a third-party app you like, but for anyone else who’s annoyed by this, you may find this keyboard shortcut helpful: when you’re in the Command-Tab app switcher, you can type Command-Down Arrow to see the individual windows of the selected app. Then you can use the Left and Right Arrow keys to select a window, and press Return to switch to that window.
This is a little fiddly mechanically, so here’s a more detailed explanation:
(On my U.S. keyboard, the Backtick key is directly above Tab. I’m not sure how and whether these shortcuts are different on different keyboard layouts.)
This seems ridiculous when I write it all out like this, but once you get it in your muscle memory it’s pretty quick, and it definitely feels faster than moving your hand to your mouse or trackpad to switch windows. (Who knows whether it’s actually faster.)
Thanks, I’ve tried this before but my issue is that this process involves a lot of hand-eye loop (look at something, decide what to do, do it). On the other hand, if I have a list of open windows, there is exactly one loop - find my window, move the mouse and click.
I hope people whose brain doesn’t work like mine find this useful though :)
It’s kind of nutty, but my fix for window switching has been to set up a window switcher in Hammerspoon: https://gist.github.com/jyc/fdf5962977943ccc69e44f8ddc00a168
I press alt-tab to get a list of windows by name, and can switch to window #n in the list using cmd-n. Looks like this: https://jyc-static.com/9526b5866bb195e636061ffd625b4be4093a929115c2a0b6ed3125eebe00ef20
Thanks for posting this! I have an old macbook that I never really used OSX on because I hated the window management. I gave it another serious try after seeing your post and I’m finding it much easier this time around.
I ended up using https://alt-tab-macos.netlify.app over your alt tab, but I am using Hammerspoon for other stuff. In particular hs.application.launchOrFocus is pretty much the win+1 etc hotkeys on Windows.
Once you factor in the longevity of mac laptops vs pcs, the value proposition becomes even more striking. I think this is particularly true at the pro level.
I use both. But to be honest, on the Linux side I use KDE plasma and disable everything and use a thin taskbar at the top and drop all the other stuff out of it and use mostly the same tools I use on macOS (neovim, IntelliJ, Firefox, etc…).
Which is two extremes. I’m willing to use Linux so stripped down in terms of GUI that I don’t have to deal with most GUIs at all other than ones that are consistent because they’re not using the OS GUI framework or macOS.
There’s no in between. I don’t like Ubuntu desktop, or gnome, or any of the other systems. macOS I am happy to use the guis. They’re consistent and for the most part. Just work. And I’ve been using Linux since they started mailing it out on discs.
I can’t tell you exactly why I’m happy to use macOS GUIs but not Linux based GUIs, but there is something clearly not right (specifically for me to be clear; everyone’s different) that causes me to tend to shun Linux GUIs altogether.
If I cared about hacking around with the OS (at any level up to the desktop) or the hardware, I wouldn’t do it on Apple kit, but I also wouldn’t do it on what I use every day to enable me to get stuff done, so I’d still have the Apple kit for that.
What can be this used for? The readme makes it very clear how one can use it but I don’t understand what are the use-cases.
I use zig for it atm (works on Windows, can cross compile, includes what is probably the best C++ build system available today) but you can commit the compiler and have an entirely self contained repo that works anywhere.
There are some downsides: zig c++
doesn’t seem to generate debug info properly so we only use it for release builds, clang is the worst of the three major compilers, GitHub won’t let you push zig.exe because it’s too big so you have to do a gzip dance (which git does internally so this saves nothing for GitHub anyway), we precompile dependencies and commit those too and libc/libstdc++ mismatch BS is annoying, convincing third party build systems to use zig instead of the host compiler is annoying, …
That’s right. But, we have a set of sysroots with libc’s ad hope to do what zig does: bundle libc & libc++ sources and build sysroots for “cross” targets on demand. It would be done with a dedicated tool (e.g. llvmbox-mksysroot) rather than integrated since llvmbox’s aim is to offer llvm vanilla; “as is.”
Does this count as dead for the purposes of the Official LastPass Death Pool?
Second, the software distribution - docker definitely made things easier to ship.
This is quite the understatement.
Linux’s approach of making everything dynamically linked and depend on everything else, combined with the general complexity explosion we’ve seen in software means Linux has degraded to the point where you basically can’t run software on it.
Static linking has solved this problem for longer than it has even been a problem, but for GNU flavoured historical reasons you can’t use it for most software. So instead people have reinvented it but worse, and the default way to ship software is to ship a tarball but worse containing a disposable operating system that only runs your app.
You still need a host OS to run the on the hardware itself, which will have a half-life measured in months before it self destructs. You can push this out if you never, ever touch it, but even as someone who has repeatedly learned this lesson the hard way (still using Firefox 52, iOS 13, etc) I still can’t keep myself from occasionally updating my home server, which is generally followed by having to reinstall it.
It really only holds when you’re talking about software which hasn’t been packaged by your host OS tho, right?
If I want to run something that’s in apt, it’s much, much easier to install using apt.
I find it’s easier to bring up a PostgreSQL instance in a Docker container, ready to go, than to install and configure it from apt. Both are pretty easy though.
I’m on the opposite side of this matter: I have a dev db, I put everything in it, single version, configured once, run since then. When I played with docker and considered how useful it could be I decided to not go that direction, because for my use-case, docker didn’t seem added value.
The difference is that you have to learn how apt works if you run an apt-based system. If you learned to use Docker for some other reason (probably for work, because why else would you?) that’s not as widely applicable.
But learning apt and learning docker, it’s still a huge difference.
If you want to do an extensive customization, you still have to learn apt to fiddle with the things in the image itself, plus a lot of docker things on top.
that’s not as widely applicable.
actually, you might argue that docker (and podman) are more applicable because what you learn there can be used on any distro running docker, whereas only knowing how to use apt limits you to only distros that use apt…
Not at all, in the last year or so I’ve had two installs with almost nothing on them (htop process list comfortably fits on 1 page) self destruct (boot into unusable state/refuse to boot) on their equivalents of apt-get upgrade.
I’d recommend trying to understand what exactly happened and what’s failing when you run into situations like that, especially if it happened more than once. Things don’t normally self destruct. Sure, you can run into a bug that renders the system unbootable, but those are pretty rare. A significant part of the world computing runs on Linux and it runs for years. If your experience is “will have a half-life measured in months before it self destructs”, it may be worth learning why it happens to you.
Wellllll… Debian systems don’t self-destruct on apt upgrade, but there are many other downstream variants that still use apt but don’t believe in old-fashioned ideas like … making sure things actually work before releasing.
Debian systems don’t self-destruct on apt upgrade
At least, not if you upgrade them regularly. I’ve hit a failure mode with older Debian systems because apt is dynamically linked and so when the package / repo format changes you end up not being able to upgrade apt
. This isn’t a problem on FreeBSD, where pkg
is statically linked and has a special case for downloading a new version of the statically linked binary that works even if the repo format changes.
Frankly, why would I?
15 years ago I probably would have. Nowadays I understand my time is too valuable for this. When I spend my time to learn something there are so many wonderful and useful ideas in the world to immerse myself in. Understanding why my almost completely vanilla OS nuked itself for the nth time after I used it normally is not one of them.
Windows and Mac both have comfortable access to the good parts of Linux through WSL/docker (WSL is by far the most unreliable thing on my PC despite not even needing to be a complete OS) while also not dropping the ball on everything else. For the one machine I have that does need to be Linux, the actual lesson to learn is to stop hitting myself and leave it alone.
Things don’t normally self destruct. Sure, you can run into a bug that renders the system unbootable, but those are pretty rare.
In other circles:
Frankly, why would I?
For me that’s: because I can do something about it, as opposed to other systems. For you the bad luck hit on Linux. I’ve had issues with updates on Linux, Windows, Macs. Given enough time you’ll find recurring issues with the other two as well. The big difference is that I can find out what happened on my Linux boxes and work around that. When Windows update service cycles at 100% CPU, manually cleaning the cache and the update history is the only fix (keep running into that on multiple servers). When macos after an update can’t install dev tools anymore, I can’t debug the installers.
In short: everything is eventually broken, but some things are much easier to understand and fix. For example the first link is trivially fixable and documented (https://wiki.archlinux.org/title/Pacman/Package_signing#Upgrade_system_regularly)
To largely rehash the discussion on https://lobste.rs/s/rj7blp/are_we_linus_yet, in which a famous tech youtuber cannot run software on Linux:
Given enough time you’ll find recurring issues with the other two as well.
This is dishonest, the rate and severity of issues you run into while using Linux as intended are orders of magnitude worse than on other OS. In the above, they bricked their OS by installing a common piece of third-party software (Steam). Software which amusingly ships with its own complete Linux userspace, another implementation of static linking but worse, to protect your games from the host OS.
because I can do something about it, as opposed to other systems
This is untrue, Windows at least has similarly powerful introspection tools to Linux. But even as someone who ships complex software on Windows (games) I have no reason to learn them, let alone anyone trying to use their computer normally.
For example the first link is trivially fixable and documented
In this case you can trivially fix it, you can also trivially design the software such that this never happens under normal conditions, but the prevailing Linux mentality is to write software that doesn’t work then blame the user for it.
This is dishonest, the rate and severity of issues you run into while using Linux as intended are orders of magnitude worse than on other OS.
It’s not dishonest. This is my experience from dealing with large number of servers and few desktops. Including the ability to find actual reasons/solutions for the problem in Linux, and mostly generic “have you tried dism /restorehealth, or reinstalling your system” answers for Windows.
This is untrue, Windows at least has similarly powerful introspection tools to Linux.
Kind of… ETL and dtrace give you some information about what’s happening at the app/system boundary. But they don’t help me at all in debugging issues where the update service hangs in a busy loop or logic bugs. You need either a lot of guesswork or the source for that one. (or reveng…)
Meanwhile, the host OSes are refusing to properly package programs written in modern programming languages like Rust because the build system doesn’t look enough like C with full dynamic linking.
What do you mean by this?
I’m a package maintainer for Arch Linux and we consistently package programs written in post-C languages without issue.
Via collaboration and sharing with other distributions, we (package maintainers) seem to have this well under control.
The whole point is that this is C++. And C++ has a global object called std::cout, which has a left shift operator. Are you suggesting that a new syntax should have made it impossible to call the left shift operator on that object..?
C++23 is about to introduce a better print function (std::print
). You can use fmtlib to get that print function today. But improving the standard library is completely orthogonal to introducing a new syntax.
Haha, I was unaware of std::print but more importantly I was unaware of std::format - habituated to *printf(…) I hadn’t been paying attention to all of the new APIs getting added to stdlib. On the downside I work in llvm/clang land so I only get c++14 :(
You can write a fmt-alike in low hundreds of lines of code with C++11 vardiadic templates or pre-C++11 1 to N overloads, or you can use ggformat
Clang has had full C++17 support since Clang 5, and it supports most of C++20 (https://clang.llvm.org/cxx_status.html#cxx20) and even a decent chunk of what will become C++23 (https://clang.llvm.org/cxx_status.html#cxx23). Things may not be as dire as you think :)
It seems like libc++ also supports std::format since version 14: https://libcxx.llvm.org/Status/Format.html#misc-items-and-todos
Haha you misunderstand, I work on them :D Clang and llvm code bases are essentially c++14. It’s super annoying :)
Operators can mean different things in different contexts. Many languages use + for string concatenation. Even in math, multiplication has a different meaning for matrices than for real numbers. Would it have been better to define some other sequence of punctuation marks for writing to a stream?
I agree that overloading the bit shift operator isn’t worse than + for concatenation, in and of itself. But iostream has so many problems: https://www.moria.us/articles/iostream-is-hopelessly-broken/. So like olliej I was disappointed seeing std::cout << "Hello"
in the example code.
Oh, I agree about iostream. But cppfront is a parser / transpiler, fully compatible with existing C++20, so implementing a whole new IO library would be rather out-of-scope, n’est-ce pas?
Isn’t that out-of-scope? The goal is to evolve C++ syntax, not its libraries.
It is also explicitly not a new language, so it makes sense to use the standard, if “horrific”, hello-world as a first example:
I’m sharing this work because I hope to start a conversation about what could be possible within C++’s own evolution to rejuvenate C++
I would have preferred adoption of something similar to template strings as we did in JS. JS engines have to deal with (parsing, compilation, and execution) far more random code than any C++ code and were able to handle the addition of template strings without a problem. I think it’s also more powerful than std::format as you can provide a function that actually has the job of construction, which is only possible because the syntax is part of the language.
You know what? It’s already there… https://github.com/hsutter/cppfront/blob/main/regression-tests/mixed-string-interpolation.cpp2
I immensely dislike this overload as I do not see anything about << or >> that makes sense. Whereas + meaning concatenation makes sense as a concatenation.
And no I’m not proposing the addition of new operators for streams, I’m proposing not using overloaded operators. I don’t think stream.write(…) is perfectly reasonable, and I personally would expect it to be less characters as well (because many people measure character to count as being inversely proportional to “goodness”). The problems with the “streaming” APIs is that they are super verbose, they break up the actual text (in the case that you’re dealing with text obviously), and that things like formatting behavior are modal so that things in the old format string APIs are obnoxious to perform.
Great article!
FWIW I’m using docker and podman with the same images and it works well. Our CI runs on two configurations the same way:
There were some minor permissions issues on the mounts, but it wasn’t hard to work around.
So I’m glad that there are two implementations, so I don’t get locked into Docker.
Some of the links led me to these lazy pulling projects with associated file formats.
https://github.com/containerd/stargz-snapshotter
https://github.com/containerd/nerdctl
I remember those from a 2016 paper, so it’s cool that there are open source implementations of it.
Although my pet peeve is that I think there shouldn’t be such things as container registries. We should just have general purpose storage with differential compression, which can store containers or other data.
For example pulling containers on sourcehut 10x slower because I guess there are no container caches on the local network, like there are in most clouds. But I think that is the “fault” of the whole tightly coupled container architecture.
Does anyone store containers in a place other than a registry? It looks like there is an optional IPFS backend, and I’m interested if anyone uses it, although my suspicion is that it probably works very poorly and slowly (since I mostly hear bad things about how well IPFS works)
I would like to experiment with storing container metadata in git and then the layers on a plain HTTP server (e.g. served by Nginx). I don’t see any reason that’s not possible? What does the container registry really add?
https://github.com/NicolasT/static-container-registry is a thing but I haven’t gotten around to trying it so no idea if it’s any good
Tools to construct a read-only container ‘registry’ served by plain Nginx
Oh nice that’s basically what I’m looking for ! It doesn’t seem widely used, which makes me think there must be some downside like performance … but it’s worth checking out and understanding why, thanks.
edit: I guess the obvious downside is being read-only, which makes sense since plain HTTP has no way to persist data. So you can’t do docker push. But yeah I still think there is some potential to get rid of docker registries.
One bummer here is that it doesn’t seem to be fully static; it relies on a bit of custom nginx config to do URL rewriting and to munge error pages;. It doesn’t result in something you could just dump into an S3 bucket or moral equivalent - I’m not sure if such a thing is even possible, or the endpoints required by the clients are such that at least a little logic is required.
There are some tools in https://github.com/ostreedev/ostree-rs-ext/ for importing and exporting container images from an ostree repo; it’s on my list of things to try out.
The registries add the distribution API. Most registries implement anything beyond the API calls that aren’t static (e.g. /v2/<name>/tags/
, various forms of auth, the push flow) by redirecting to static storage.
Container registries are general purpose storage if you squint (and there are people working on making it “official”), with the added benefit of being able to programmatically understand how the pieces are related.
Check also cvmfs https://cvmfs.readthedocs.io/en/latest/cpt-containers.html
One that was missed, which I’ve seen only in libICU. Every function takes an UErrorCode*
, which must be initialised to no-error. If the function encounters an error then it will set the error state. In addition, all of the functions check the error state on entry and return immediately if it is an error. This lets you write exception-like control-flow in C, something like:
UErrorCode err = NO_ERROR;
someFn(arg, anotherArg, &err);
secondFn(arg3, &err);
lastFn(something, &err);
if (IS_ERROR(err))
{
// Handle the error now
}
You can turn this inside out too:
bool ok = true;
ok = ok && f1();
ok = ok && f2();
ok = ok && f3();
if (ok) etc
You can, if the error return is a boolean. If it’s a richer type then it ends up being more verbose, something like:
ErrorCode ok = NO_ERROR;
ok = IS_ERROR(ok) ? ok : f1();
ok = IS_ERROR(ok) ? ok : f2();
ok = IS_ERROR(ok) ? ok : f3();
if (IS_ERROR(ok)) etc
You can hide that in macros, but it gets a bit more hairy. The nice thing about putting it inside the function is that chained calls don’t duplicate the error checking code and a modern branch predictor will very rapidly learn to not expect the error condition and so the extra 2-3 instructions inside each function are basically free: they’ll be predicted not-taken most of the time and just consume a small amount of i-cache (less than they would on the outside of the function at every call site).
This is missing png -0 + zstd (png -0 still applies some filters designed to make the data compress better) which is probably competitive (zpng is similar), and Oodle Lossless which trashes on everything
The most important metric is missing. When I am creating website then I do not really give a damn about compression duration. It is meaningless as I do it only once when deploying. Even if it would take minutes it is not an issue, as I do it so infrequently. On the other hand decompression duration is all what I care. It will be done thousands times per day by the machines I really care about - my users machines. If I can have small file that will be fast to decompress then I really do not care how long it will take me to compress that file to this state.
So it is all fun and giggles, but the most important metric is missing, which is weird.
Fortunately in all cases the decompression is fast enough that it’s not an issue. Even if you’re concerned with mobile devices, the energy cost of downloading data is so much higher than decoding that it still always makes sense to focus on the data size.
Also the better compressed the input is, the faster decompression tends to be. This is because decompressors are like interpreters of instructions, and smaller file means fewer instructions to execute (less work for entropy decoder).
Fortunately in all cases the decompression is fast enough that it’s not an issue.
In all cases so long as it’s a single use download :)
$100 SSDs can read data at 5GB/s+ (which will decompress to some multiple of that), and consensus amongst PC building people is that it makes no difference vs SATA SSDs which are 10-20x slower because software can’t keep up
Also the better compressed the input is, the faster decompression tends to be.
This is less true than you think. e.g. memcpy is the fastest decoder
The article compares Web-oriented formats, so I was talking about Web usage.
Even for SSDs there are beneficial compression methods, they just need different codecs (PS5 and DirectStorage still use compression).
memcpy
isn’t the fastest decoder. At CPU speeds, memory is very slow. It can be faster to store things compressed in memory and decompress them on the fly every time when accessing them, because it saves on memory bandwidth and the CPU has lots of cycles to spare while waiting for memory. The same applies to GPUs: they use compressed textures in memory and decompress a texture block every time they sample a texel, because that’s still faster than extra memory bandwidth.
In general, we recommend regularly auditing your dependencies, and only depending on crates whose author you trust.
Ok thanks?
I can do this in C++, because the language makes it impossible to ship libraries that have dependencies and you don’t get the exponential blowup, but the Rust ecosystem makes this completely intractable.
Last time I messed with rust, I wrote a very basic piece of software (connect to Discord, FlashWindow if anyone says your name, like IRC highlights) and it pulled in over 1 million lines of dependencies. Even if I could audit that, what can I do if I find a crate I don’t like?
I think this is a false dichotomy, as outlined by (as just one example) https://wiki.alopex.li/LetsBeRealAboutDependencies
C/C++ stuff has dependencies, and sometimes enormous amounts of them. This mantra that dependency tree hell is an NPM/Cargo/etc. specific problem is - frankly - ridiculous. I can’t audit all of, say, Boost or LibreSSL much better than I can audit, say, Tokio or RustTLS. Arguably, it’s harder to audit something like LibreSSL than RustTLS on account of the memory management, but that’s a vastly different discussion. Let’s imagine I’m instead talking about NodeJS’s “lodash” or something, which I also haven’t read every line of.
But you have package managers as a line of defense with C/C++ and most other popular languages. I don’t know why this is so hard to understand for many people.
some C++ libraries have very high standards for contribution to them and the overall review + release process. C++ Boost is one of them ( https://www.boost.org/development/requirements.html ).
Perhaps in some ways, the argument can be made that one big (but composable/de-composable), thoughtfully managed library with high-bar for entry (like Boost) – in longer term is significantly better, than 100s or 1000s of crates or NPM packages (also helps that the quality of library informs upcoming language standards).
The article you linked, to me, reads a bit more like false dichotomy (as it does not take into account the maturity of both technical and release process maturity characteristics of the C and C++ libraries).
These complaints are valid, but my argument is that they’re also not NEW, and they’re certainly not unique to Rust. Rather the difference in dependencies between something written in Rust vs. “traditional” C or C++ is that on Unix systems, all these dependencies are still there, just handled by the system instead of the compiler directly
If you want something that’s not Linux/Intel only and has a non-web GUI for viewing traces (which is a nice way of saying won’t choke on traces larger than a few MB), I can very highly recommend tracy
I feel like it’s my obligatory “use Vivaldi” comment. The core functionality of Chromium is really great from a technical perspective. What you don’t want is all the “google phone home” nonsense. So just don’t use it. Vivaldi comes with ads/trackers blockers built-in.
Ungoogled Chromium is a much better choice for addressing the concerns expressed in the webcomic.
Ah sorry, I usually see people shilling for Brave in these kinds of discussions, so I mixed them up.
I really don’t think a cryptocurrency scam closed-source proprietary browser is the answer here tbh.
Chrome and Firefox are open-source on paper only. You have no say in how they’re developed unless you work for a trillion dollar advertising company, can’t contribute code unless it’s your full time job, and can’t ever hope to audit even a tiny fraction of the tens of millions of lines of code even if it’s your full time job.
Can’t comment on Chrome, but for Firefox I can personally tell you that is not true. I scratched my own itch in the developer tools when I was in high school. Was it easy? No. Was it impossibly difficult? Also no.
(In fairness though this was easier with the developer tools than with, say, Gecko core.)
Their explanation of why their UI is not open source. To be perfectly honest, though, you’re clearly not coming from a place of honest exploration or debate.
I’m coming from a place of dismissing closed-source browsers. I don’t think that’s unwarranted. We have really good open-source browsers.
When the concern is that Chrome is phoning home and invading your privacy, it seems absolutely bonkers to me to suggest that switching to another closed-source browser is the solution.
At this point you seem to have an axe to grind. We get it. FOSS is good and crypto is bad here on Lobsters.
Serious question: aren’t cryptocurrencies subjectively bad? Some waste energy, some things don’t work, a lot of things are scams., the main use is for illegal trades. Is there something amazing somewhere that I am missing?
We’re going far off-topic from the OP so I don’t think there’s value in starting that discussion here. If you’d like to discuss the topic, I’m always open to DMs discussing stuff, though I get busy and may take time to respond.
It’s somewhat worse than that: a non-admin user can run code (as you) on your computer if it uses the defaults on Windows. c:\
is world writable by default 😞
And any shared location for git repositories where multiple users have write access could allow one of the users to hijack the account of any of them.
It’s worse than that. If you run git commands in a directory, anyone with write access to any directory higher up the tree than that can run arbitrary code as your user. If, like me, you have your $PS1
set to run some git status
/ git remote
commands so that your prompt can tell you what git remote tree you’re working with and what branch you’re on, then just cd
into a directory where a malicious user has write access somewhere up the tree can run arbitrary code as that user. Even without that, if someone can ask you to run git status
on a tree that they control, they can run arbitrary code as your user.
And yet linux still doesn’t have a good UI for all these fancy options. :(
I think the JetBrains tools provide a pretty good one. Or at least PyCharm and Clion do. Honestly, those two debugger UIs are half the reason I’m willing to pay their toolbox renewal every year.
The integration between the python repl and the debugger UI feels like pure magic. And being able to step from python into C++ and back has been wildly useful in the past too.
I tried all of the gdb wrappers ~5 years ago and cgdb was by far the best. You might have to compile from git because they don’t release very often.