I think this article has "aged well" in the sense that... nothing has changed for the better :( Since I wrote it, I did upgrade my machine: I now have a 24-core 13th Gen i7 laptop with a fast NVMe drive and... well, Windows 11 is _still_ visibly laggy throughout. Comparing it to KDE on the same machine is like night and day in terms of general desktop snappiness (and yes, KDE has its own bloat too, but it seems to have evolved in a more "manageable" manner).
I've also gotten an M2 laptop for work since then, and same issue there: I remember how transformative the M1 felt at launch with everything being extremely quick in macOS, but the signs of bloat are _already_ showing up. Upgrading anything takes ages because every app is a monster that weighs hundreds of MBs, and reopening apps after a reboot is painfully slow. Still, though, macOS feels generally better than Windows on modern hardware.
About the article itself, I'll say that there was a complaint back then (and I see it now here too) about my blaming of .NET rewrites being misplaced. Yes, I'll concede that; I was too quick to write that, and likely wrong. But don't let that distract yourself from the rest of the article. Modern Notepad is inexplicably slower than older Notepad, and for what reason? (I honestly don't know and haven't researched it.)
And finally, I'll leave you with this other article that I wrote as a follow-up to that one, with a list of things that I feel developers just don't think about when writing software, and that inevitably leads to the issues we see industry-wide: https://jmmv.dev/2023/09/performance-is-not-big-o.html
There was an attempt to make a Chrome OS competitor that had the entire UI rewritten in UWP, and when that got canceled it seems MS saved the new start menu + taskbar and bolted it on top of Win10, and for Explorer and other apps made this UWP+Win32 abomination. Actually when you profile Explorer you can also see DirectUI running, which is I believe another UI framework from the Office org. this time (maybe it's related to OneDrive).
Btw, apps written in C# using WPF can be surprisingly fast, so it's not really a problem of .NET managed vs. native apps. For ex. may favorite Git GUI (git-fork.com) is C#/WPF.
I agree with him though. I recently had a machine that I upgraded from Win10 to Win11 and it was like someone kneecapped it. I don’t know if it’s modern app frameworks, or the OS, but something has gone horribly wrong on macOS and windows (iOS doesn’t suffer from this as much for whatever reason IME)
My gut instinct is an adjustment to everything being asynchronous, combined with development on 0 latency networks in isolated environments means that when you compound “wait for windows defender to scan, wait for the local telemetry service to respond, incrementally async load 500 icon or text files and have them run through all the same slowness” with frameworks that introduce latency, context switching, and are thin wrappers that spend most of our time FFI’ing things to native languages, and then deploy them in non perfect conditions you get the mess we’re in now.
Unity is the great battery killer!
The last example I remember is that I could play the first xcom remake (which had a native mac version) on battery for 3-4 hours, while I was lucky to get 2 hours for $random_unity_based_indie with less graphics.
This really shouldn't be slower when done asynchronously compared to synchronously. I would expect, actually, that it would be faster (all available cores get used).
And I think this assumption is what's killing us. Async != parallel for a start, and parallel IO is not guaranteed to be fast.
If you write a function:
async Task<ImageFile> LoadFile(string path)
{
var f = await load_file(path);
return new ImageFile(f);
}
And someone comes along and makes it into a batch operation; async Task<List<ImageFile>> LoadFiles(List<string> paths)
{
var results = new List<ImageFile>();
foreach(var path in paths) {
var f = await load_file(path);
results.Add(ImageFile(f));
}
return results;
}
and provides it with 2 files instead of 1, you won't notice it. Over time 2 becomes 10, and 10 becomes 500. You're now at the mercy of whatever is running your tasks. If you yield alongside await [0] in an event loop, you introduce a loop iteration of latency in proceeding, meaning you've now introduced 500 loops of latency.In case you say "but that's bad code", well yes, it is. But it's also very common. When I was reading for this reply, I found this stackoverflow [0] post that has exactly this problem.
[0] https://stackoverflow.com/questions/5061761/is-it-possible-t...
It would be interesting to see something like:
- cold boot
- load Windows
- load Office
- open a 200 page document
- create pdf from said document
- open 5000 row spreadsheet
- do a mail merge
- open 150,000 record database
- generate some goofy report
Do people still do mail merge? Not sure.
It ran (5-10x) a set of scripted operations in Word, Excel, PowerPoint, Access, PhotoShop, and WinZip, on large files, and that was the basis of of the benchmark score.
I ported the benchmark suite from 16-bit Windows to 32-bit Windows.
> The author mentions rewriting core applications in C# on windows but I don’t think this is the problem. Write a simple hello world app in c#, compile it and see how long it takes to run vs a rust app or a python script - it’s almost native <...>
> My gut instinct is an adjustment to everything being asynchronous, combined <...> with frameworks that introduce latency, context switching, and are thin wrappers that spend most of our time FFI’ing things to native languages, and then deploy them in non perfect conditions you get the mess we’re in now.
In 2002 I ran OpenBSD on my laptop (thus sacrificing wifi). The memory footprint of running X11, a browser, a terminal, and an editor: 28MB
When size is not an issue, it's harder to say no when the business demand for a telemetry system, an auto-update system, a crash handler with automatic report, and a bunch of features, a lot of which needs to be initialized at the start of the program, introducing significant latency at startup.
Take font rendering: in early machines, fonts were small bitmaps (often 8x8 pixels, 1 bit/pixel), hardcoded in ROM. As screen resolutions grew (and varied between devices), OSes stored fonts in different sizes. Later: scalable fonts, chosen from a selection of styles / font families, rendered to sub-pixel accuracy, sub-pixel configuration adjustable to match hw construction of the display panel.
Yeah this is very flexible & can produce good looking fonts (if set up correctly). Which scale nicely when zooming in or out.
But it also makes rendering each single character a lot more complex. And thus eats a lot more cpu, RAM & storage than 8x8 fixed size, 1bpp font.
Or the must-insert-network-request-everywhere bs. No, I don't need search engine to start searching & provide suggestions after I've typed 1 character & didn't hit "search" yet.
There are many examples like the above, I won't elaborate.
Some of that complexity is necessary. Some of it isn't, but lightweight & very useful. But much of it is just a pile of unneeded crap of dubious usefulness (if any).
Imho, software development really should return to 1st principles. Start with a minimum viable product, that only has the absolute necessary functionality relevant to end-users. Don't even bother to include anything other than the absolute minimum. Optimise the heck out of that, and presto: v1.0 is done. Go from there.
Not millions of times more complex.
Except for some outliers that mess up everything (like anything from Microsoft), almost all of the increased latency between keypress and character rendering we see on modern computers comes from optimizing for modularity and generalization instead of specialized code for handling the keyboard.
Not even our hardware reacts fast enough to give you the latency computers had in the 90s.
Modern example: Laptops boot in seconds. My servers take about 5 minutes to get to Linux boot, with long stretches of time taken by various subsystems, while Coreboot (designed to be fast) boots them nearly as quickly as a laptop.
Old example: early in my career we were developing a telecom system with a 5 min per year (5 9s) downtime target. The prototype took 30 minutes to boot, and engineers didn’t care because management hadn’t told them to make it boot faster. It drove me nuts. (a moot point, as it eventually got cancelled and we all got laid off)
We now have HUGE (/s) advancements in Notepad, like tabs and uh... Copilot
When performance supersedes "more features", developers are gatekeepers and manager initiatives can be re-examined. The "solution" is to make performance a non-priority and paint complainers as stale and out-of-fashion.
Those 2 goals align about as often as planets in our solar system.
To some degree this is true for open source software as well. Developers may choose to work on features they find interesting (or projects done in a language they feel comfortable with), vs. looking at user experience first. Never mind that optimizing UX is hard (& fuzzy) as it is.
Or all the work done on libre software to cater to various corporate interests. As opposed to power users fixing & improving things for their needs.
What is an unspoken assumption in this type of complaint is that the software would still have gotten written if it had to be more handwritten. The amount of projects I've seen beached on an outdated version of Spring because they neglected upgrading even patch releases convinces me that more effort is just not tenable.
Things like summoning a keyboard causing my 120hz galaxy phone to drop to sub 10fps playing the intro animation for GBoard were just rampant. All non existent in iOS
Edit: oh, no, you have a point about the UI blocking stuff, it's fine when apps are loaded and active but "cold booting" a UI component definitely has lags in stupid places, android UX feels like a web perform sometimes due to that.... Tap button, go on holiday for a week, come back and it's responded to the button press (while you were trying to do something completely different and now you've pressed something else and you're not sure what because this time the button you pressed closed the activity overlay 1ms after)
Some things are slow, like discord on windows takes ~12 seconds before it starts to pop in ui elements after you double click. My main computer is beefy, though, 32 thread 128GB; but no NVMe. Sata spindle and SSDs. But I have Ryzen 3600s that run windows 11 fine.
These days you aren't just opening a 64k executable (notepad), you're calling back to the mothership, recording usage data, blah blah
Personally I've decided to just vote with my feet and avoid using poor performing software as much as possibl, but that's frequently impractical or not worth the cost of missing out. I also doubt this will change the behaviors of companies as we see with, for example, TV advertising that they give no shits about degrading the consumer experience over the long term.
There doesn't seem much hope on the technical side either as software complexity is only increasing.aybe longer term AI has a role to play in auto-optimization?
Google Authenticator’s filter box, when you tap it there is a very noticeable delay after tapping the filter box and the keyboard showing.
And what makes it worse is that if you switch away from the app, it auto clears the filter.
This isn’t a complex app and it’s slow at doing a use case easily performed millions of times a day.
Every time I try to use it, the UI elements shift around as I try an action as simple as activating a search box. But it is just laggy enough that you click on something a second time, since it apparently didn’t activate the first time. But by then it’s shifted everything around and you are either cancelling what you were doing, or you are taken off into some new workflow you didn’t want.
The absolute WORST is when you focus the search box and it shifts to the top of the screen. With lag, you click on the search box again, but now where it was is a list of completion suggestions and you are taken off to some search result page you never asked for.
It’s fucking infuriating and I won’t entertain it.
The applications are launched at startup time because they have runtime startup slowness. The applications have startup slowness because of the JIT runtime/deps/.dll.
At the end of the day, end users pay for the cost of developer convenience (JIT and deps most of the time, even thought there are some case where dynamic linking is alright) because they don't do native apps.
Offloading everything at startup is a symptom IMO.
Replying to your specific point about virus scans. For some (naive) reason, I expect them to run a single time against a binary app that is never changed. So in theory it shouldn't even be a problem, but the reality says otherwise.
Playing devil's advocate: the executable might not have changed, but the database of known virus signatures changes daily or more often. Either every single executable would have to be re-scanned every time the database updates, or the executable has to be lazily scanned on load.
Bloat!
It was pretty easy indeed.