r/programming 2d ago

The Browser's Main Thread Is Expensive

https://kciter.so/posts/the-expensive-main-thread/en/
212 Upvotes

48 comments sorted by

13

u/Somepotato 2d ago

Omitting shared memory buffers and atomics when discussing workers is a little strange.

Workers can also render things to canvases off the main thread.

79

u/Rhed0x 2d ago edited 2d ago

Good article.

One day JS will reach the 21st century and support proper multithreading. Obviously won't help for UI updates but for things like parsing large files.

Workers are a massive pain in the ass, JS should just share memory with additional threads and get proper synchronization primitives like every language.

But what can you expect from a programming language that doesn't even have integers.

31

u/birdbrainswagtrain 2d ago

It would be neat to see proper multi-threaded JS but I assume the amount of engineering and security considerations massively outweigh whatever benefits we'd get out of it. I'm still fighting obnoxious restrictions on SharedArrayBuffer.

If you're the rare person who actually needs to write a multi-threaded web application, and who needs shared state (message passing is a great approach for multi-threading which I suspect works for 90% of applications) I think you should probably just bite the WebAssembly bullet and use C++ or Rust.

12

u/Rhed0x 2d ago

I already avoid JS but unfortunately it's used for most desktop applications these days. I just wish those wouldn't all run like crap.

2

u/Chii 1d ago

I just wish those wouldn't all run like crap.

tbh, i dont think it is JS inherently at fault. If the app runs like crap, it's because the authors of those apps don't care.

1

u/Rhed0x 19h ago

So everyone who writes JS doesn't care? Got it.

1

u/Chii 19h ago

vscode runs fast (fast enough). Not every js app runs slow. Those that run slow are that way because the authors didnt care.

3

u/Shoddy-Childhood-511 2d ago

A priori, I'd think multi-threaded JS would enable stupidity that killed the CPU entirely.

Instead, we could throttle the shit out of JS maybe? Alternatively we could've certificate infrastructure for "We promise not to hog your CPU or else we'll loose this certificate forever" for sites that wanted a bit more CPU time, but that feels overly complex vs simply throttling all JS.

Anyways, multi-threaded JS or WASM could perhaps be exploited to hack your system, using techniques like rowhammer. Browsers should probably isolate multi-threaded WASM to one physical core, or roll back threads support entirely.

I should find the firefox version for Chrome's '--disable-webassembly-threads'. lol

1

u/TheRealUnrealDan 2d ago

I heard once upon a time that spectre could be triggered from js, imagine a website reading your password or private keys or something :'l

1

u/aiusepsi 1d ago

Alas, Wasm and C++/Rust doesn’t free you from SharedArrayBuffer problems, because threading in WebAssembly means creating web workers, and letting each WebAssembly instance in the workers use the same SharedArrayBuffer as shared memory.

1

u/AdditionalDirt223 12h ago

That's a good point. WebAssembly does have its limitations with threading, especially when it comes to shared memory access. It can get pretty complicated trying to manage that effectively across different instances.

17

u/SargoDarya 2d ago

You can already use a SharedArrayBuffer with postMessage though? Also it has BigInt (which isn’t int, I get that but you get the idea)

7

u/Rhed0x 2d ago

That requires manually converting stuff into PODO, why can't you simply pass JS references across to a worker.

6

u/Full-Spectral 2d ago

It is just all GC'd data and I'd think it would be easy enough. But, they'd have to ensure that nothing inside that reference, no matter how deep, was still visible to the sender. With the simplified scenarios they provide currently, that's doable. But unless they are going to create Rustscript, it would probably be hard to do with arbitrary objects.

2

u/Rhed0x 2d ago

they'd have to ensure that nothing inside that reference, no matter how deep, was still visible to the sender

Or they add Mutexes, Semaphores, etc and objects can just be shared across threads like in every other language.

2

u/Full-Spectral 2d ago

It would have to be done at an automagical level probably, given the nature of Javascript and the experience of a lot of developers of such. Otherwise, you'd have web sites seizing up right and left when they deadlocked themselves.

1

u/danielcw189 2d ago

that doesn't really answer the problem of sharing references, which is something that has to be avoided, if I understand the other comment correctly

1

u/Rhed0x 2d ago

And why does it have to be avoided?

1

u/NenAlienGeenKonijn 2d ago

That assumes javascript is meant for complex software development rather than simple DOM scripting tasks to enhance your website.

Nobody would ever use javascript like that, right?

1

u/Rhed0x 1d ago

Right... :(

1

u/abraxasnl 2d ago

Because then you would need locking mechanisms, which are waaaay to easy to get wrong for users.

1

u/Full-Spectral 1d ago edited 1d ago

But it's worse than that. Without the kinds of mechanisms that Rust provides, the fact you locked foo doesn't prevent foo from having references to other things that are still being directly accessed by other stuff. There would be no reasonable mechanism to transitively ensure that locking foo actually makes foo thread safe. Or it would have to limit such locking to things that have no internal references.

Of course the same is true for C++, but at least in the C++ world they are going to be more cognizant of such issues and you don't have other people's code from all over the world running inside your C++ program either.

Honestly, a message passing scheme is not a bad deal in such cases and something I implement in Rust all the time. Even though Rust makes shared data safe, it doesn't make it more necessarily understandable; so, when an 'actorish' scheme is possible, it's not a bad choice even when you have safe data sharing. Even if you don't end up locking up the UI thread, even short blocks will make it seem sluggish.

You could always do the behind the scenes stuff in WASM and have it be nice and safe and use message passing for the main thread I guess (with a buffer pool.)

1

u/Somepotato 2d ago

Because that entails locking which can deadlock the main thread. Requiring you to be more explicit about it removes deadlocks as a risk. Plus the jit can make a number of optimizations when it knows someone rude can't step in and do a read unexpectedly.

1

u/Rhed0x 2d ago

Java and C# have optimizing JITs despite support for multithreading. And I don't really see the problem with deadlocks, it's a bug like any other. 

3

u/ccapitalK 2d ago

I'm not sure if I'm looking forward to that future tbh. Dev/Product apathy has resulted in a lot of the web being slow and bloated while being restricted to message passing concurrency, which is easier to reason about. I wouldn't be surprised if shared variable concurrency being available just results in that + deadlocks by lazy devs.

1

u/tukanoid 1d ago

Well, if that forces them to write better code and have proper Q&A.....

Not saying it won't be a headache for consumers if they push a turd to prod, but at least there will be incentive for them to get heads out of their asses and lock in, or deservingly lose their job if they're incapable of fixing their shit (not talking about juniors who haven't accumulated enough knowledge and experience, specifically talking about so-called "seniors" that only managed to get to their position by bullshitting their way through by making barely working websites/webapps where their non-tech-savvy clients or management don't realize how bad of a quality the code actually is)

1

u/Uristqwerty 1d ago

I think they were working on multithreaded Javascript a decade ago.

Then Spectre/Meltdown hit.

Turns out having two threads sharing mutable data structures lets you generate very precise timing metadata. The sort of thing you need to read CPU cache side-channels.

1

u/azhder 2d ago edited 2d ago

What you consider proper is just how you trained yourself (or were trained) to look at things. JS is a language created to be embedded into other software. The moment you add “proper multithreading” whatever it may mean to you, it will be harder to incorporate it into other software that has its own ideas of what “proper multithreading” means.

Long time ago, a language came out that didn’t have its own I/O, but depended on libraries and the OS to provide it. It allowed it such a flexibility that it is still one of the most used languages. I am talking about C.

JS, like C, didn’t come with own I/O and doesn’t come with own multithreading. That’s the price for being ubiquitous. Oh, and multithreading isn’t a language feature of C, right? It is provided by the environment (correct me if I am wrong).

So, maybe ask for better environment, not the language to take upon itself to solve the environment.

0

u/Rhed0x 2d ago

Unfortunately most software today is written in the little embedded scripting language.

64

u/sionescu 2d ago edited 2d ago

It shows how much the DOM and Javascript are garbage compared to the desktop C++ frameworks. I remember going over a Qt tutorial long time ago, which was implementing a spreadsheet, and the code has a simple loop where at the end of each line, it called commit() - the equivalent of yielding - but it was short, easy to read and performant; whereas this whole article spends an inordinate amount of words discussing the finer points of how to yield in various situations, because there's not built-in that works well out of the box. Madness.

15

u/azhder 2d ago

The browser is a desktop C++ framework that allows you to supply it with addons written in JS to manipulate its data via DOM API. We can argue how good or bad those decisions were, but just because it fetches some more data than the rest of them over the network, it doesn't make it all that different.

Remember all those plugins from the past, like Flash? They were also desktop programs that acted as if they were a part of the web page, but they were outside the sandbox and could do a lot of damage. So, that was also a decision - to sandbox everything, discontinue plugins.

All in all, taking Qt as a representative of "desctop C++ frameworks" was also a choice. It surely does make for a false dichotomy.

6

u/dignityshredder 1d ago

"Ah but the browser is a desktop C++ framework"

Insane pedantry

2

u/azhder 1d ago

Such a nice thing to call people or things you disagree as insane. It wins every argument every time. Bye

7

u/sionescu 2d ago

 The browser is a desktop C++ framework that allows you to supply it with addons written in JS to manipulate its data via DOM API.

It's not: it has a host of primitives implemented in C++ but a lot of rendering requires effectively interpreting programs written in JS or CSS instead of punting to a themeing engine written again in C++, and that can get very slow. Additionally, the DOM doesn't have a clear way of implementing performant rendering, as is evinced by the lack of a built-in yield() shown in the article. It's all a Hodge-podge of APIs with no head nor tail, all done in the name of backwards-compatibility instead of having different editions of the API, like Android has for example.

 All in all, taking Qt as a representative of "desctop C++ frameworks" was also a choice.

Yes, it was a very good choice. I've also used, in the past, GTK+ 1 and 2 (in C), Gtkmm, GNOME 1 and 2, wxWidgets on Linux and Windows, Fltk, e17, GNUstep, Tcl/Tk, even raw Win32. All of them had mechanisms for writing performant code (while being more or less verbose, having issues eith widget positioning or constraints, etc...), but the web platform still doesn't have that.

-3

u/azhder 2d ago

No, a lot of rendering does not require effectively interpreting programs written in JS. Maybe it does in CSS, but that's an interesting example, in the post you dissed. Did you see the CSS declared animation not block while the JS was blocked? It's because the rendering happens outside the JS thread.

You're trying to create a false dichotomy. It was not a good choice. Well, let's say it would have been a good choice if you were comparing desktop C++ frameworks of which the browser is one. In that case, you'd have to explain how Qt allows you to add functionality that isn't baked in to it, analogous to what the browser does with JS and CSS and now WASM.

If you do that, I'd be interested to learn more about how Qt works. Otherwise, you're barking at the wrong tree.

-4

u/sionescu 2d ago edited 2d ago

I'm not creating any false dichotomy, I'm putting into evidence the reality of things: take any C++ framework and the most natural and recommended way of writing code will be very performant (including the use of threads or some task pool system that uses threads to relieve the main rendering thread). Yet, doing the same on the web platform is so obscure that almost nobody is doing it. Websites perform like shit on a low end phone, while native apps have no problem.

4

u/dr3aminc0de 2d ago

Yeah I am not seeing a false dichotomy on your argument. And I agree. Doesn’t have to be C++ either, I’ve written plenty of local Golang Mac apps for performance too.

2

u/sionescu 1d ago

It may very well be possible to write performant web apps, but it's so difficult and unintuitive that very few manage to do so. It requires such a deep knowledge of the internals of browsers that it's no wonder.

-3

u/azhder 2d ago

“I’m putting into evidence the reality of things”… Really? If it is the reality, it is already an evidence, but you’re trying to present your own warped opinion as reality.

Look, you can call the word salad a fruit salad, it will not automatically gain vitamins. You can call your truth reality, that doesn’t mean it is, nor that it even is congruent to reality.

You want me to take a C++ framework. Well, Chromium. It is so full of (features/itself) that it is even an OS. But that doesn’t matter to you. You like to rename things to fit your own sour narrative. The browser isn’t the web platform, it is a part of it, but it isn’t only a part of it - it is its own desktop C++ framework in its own right.

Well, I don’t see us agreeing, didn’t from the start, but at least I thought you could understand me telling you that you should make a better comparison browser vs Qt, as equal frameworks for UI. Not anymore, so I will stop here. Bye

1

u/sionescu 1d ago

Bye, mofo.

3

u/BibianaAudris 1d ago

I have a higher respect of this article. Native performance just delays the problems, not solving them. CUDA SDK 0.8 had a particle demo similar to the one in the article with much more particles and exactly the same problem: CUDA used to block all display update just like JS blocks DOM. We needed the same per-frame compute budgeting trick to make it smooth back then.

Many of the discussed tricks have applications in higher-performance programming too, just with a less rigid "main thread" designation. And they took the effort to illustrate everything with a nice little live demo.

6

u/abofh 2d ago

Wait, next you'll tell me spin loops waste cpu

1

u/fireatx 1d ago

This is why I’m very interested in Servo. So much pain in web dev seems to stem from the single threaded nature of displaying websites and executing JavaScript

1

u/randfur 1d ago

Servo won't mean a change in web API standards.

-10

u/[deleted] 2d ago

[deleted]

24

u/TankorSmash 2d ago

Did you get the AI to output it like this, or was it manually done?

10

u/Blashtik 2d ago

1 week old account. I doubt there's even a human in the loop.

17

u/occasionallyaccurate 2d ago

ai writing patterns detected