11 ms·
Maybe you don't need Rust and WASM to speed up your JS (2018)
- cogman10 5y agoJavascript JITs are really good, you (likely) aren't going to see major performance improvements by dropping into WASM. That said, one major benefit of WASM that Javascript jits will have a hard time competing with is GC pressure. So long as your WASM lib focuses on stack allocations, it'll be real tough for a Javascript native algorithm doing the same thing to compete (particularly if there's a bunch of object/state management). For a hot math loop doing floating point calcs (mandelbrot calc), however, I've seen javascript end up with identical performance compared to WASM. It's really pretty nuts.
- tyingq 5y ago>aren't going to see major performance improvements by dropping into WASM I think that might also change as they add proposal and roadmap items like "direct access to the DOM"[1] to WASM. Proposals: https://github.com/WebAssembly/proposals https://github.com/WebAssembly/proposals Roadmap: https://webassembly.org/roadmap/ https://webassembly.org/roadmap/ [1] https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md https://github.com/WebAssembly/interface-types/blob/master/p... Edit: Overall, the proposals seem to be pushing WASM closer to being a general purpose VM (VM as in JVM, not KVM).
- OskarS 5y ago> Javascript JITs are really good, you (likely) aren't going to see major performance improvements by dropping into WASM. A javascript JIT is never going to be able to compete with codegen from a low-level statically typed language running through an optimizing compiler. I mean, this very article contains the perfect example: by manually inlining the comparison function they got huge performance gains from a sorting function. That is child’s play for GCC or LLVM (it’s the whole point of std::sort in C++).
- cogman10 5y agoThere are certainly nasty edges to javascript programming and I'm not trying to say that it will always be near the same performance as a WASM implementation or a statically compiled binary. What I'm saying is that you'll find more often than not that it gets close enough to not matter. You'll see javascript at or below 2->3x the runtime of a C++ or Rust statically compiled implementation in most cases. That is pretty close as far as languages go. Java is right in the same range (maybe a little lower).
- derefr 5y agoI don’t know about the “low-level” part. I have a feeling you’d get just as much of a win from a HLL statically-typed language, like Haskell. It’s the static-typing, not the low-level-ness, doing most of the heavy lifting in making code JITable/WPOable. You don’t need to manually inline a comparison function, if the JIT knows how to inline the larger class of thing that a comparison function happens to fall into, and if the code is amenable to that particular WPO transformation. I would compare this to SQL: you don’t optimize a SQL query plan by dropping to some lower level where you directly do the query planner’s job for it. Instead, you just add information to the shape of your query such that it becomes amenable to the optimization the query planner knows how to do. That will almost always get you 100% of the wins it’s possible to get on the given query engine anyway, such that there’d be nothing to gain by writing a query plan yourself.
- cogman10 5y agoSo, you'd think static typing was a major win but it actually isn't (surprisingly) for a JITed language. Most of the benefits of statically typed languages comes from memory layout optimizations. However, that sort of layout optimization is something that every fast JITed language ends up doing. This is why, for example, javascript in Graal ends up running nearly as fast as Java in the same VM. The reason it isn't just as fast is the VM has to insert constraint checks to deopt when an assumption about the type shape is violated. https://www.graalvm.org/javascript/ https://www.graalvm.org/javascript/
- unrealhoang 5y ago
- masklinn 5y ago> Javascript JITs are really good, you (likely) aren't going to see major performance improvements by dropping into WASM. The original essay shows exactly that, the one here shows pretty epic optimisations (and needs for an excellent knowledge of the platform) to get to "naive" WASM performances.
- jerf 5y ago"Javascript JITs are really good, you (likely) aren't going to see major performance improvements by dropping into WASM." If that were true, nobody would be working on WASM because there would be no point. "For a hot math loop doing floating point calcs (mandelbrot calc), however, I've seen javascript end up with identical performance compared to WASM. It's really pretty nuts. " That's easy mode for a JIT. If it can't do that it's not even worth being called a JIT. That's not a criticism or a snark, it's a description of the problem space. The problem is people then assume that "tight loops of numeric computation" performance can be translated to "general purpose computing performance", and it can't. I have not seen performance numbers that suggest that Javascript on general computation is at anything like C speed or anything similar. I see performance being more rule-of-thumb'd at 10x slower than C or comparable compiled languages. Now, that's pretty good for a dynamic scripting language, which with a naive-but-optimized interpreter tends to clock in at around 40-50x slower than C. The competition from the other JITs for dynamic scripting languages mostly haven't done as well (with the constant exception of LuaJIT). But JIT performance across the board, including Javascript, seems to have plateaued at much, much less than "same as compiled" performance and I see no reason to believe that's going to change.
- staticassertion 5y ago> If that were true, nobody would be working on WASM because there would be no point. Wasm has much more potential than just optimization. It opens up capabilities like: * Using other languages besides Javascript, or languages that just compile to JS * Passing programs around vs passing data around * Providing a VM isolate abstraction that's embedable in your existing language/ runtime
- UnpossibleJim 5y ago|* Using other languages besides Javascript, or languages that just compile to JS| This might be the most under appreciated comment I have seen all day. While Typescript did some wrangling to bring a bit a bit of "normalcy" to Javascript, the consistency that C++/Rust bring from a coding language point is (for me) enough to have gottem me interested in the use of WASM. Maybe I'm just lazy or have a strong dislike of the headache I get from learning JS compared to switching over to other languages.
- julosflb 5y ago"Javascript JITs are really good, you (likely) aren't going to see major performance improvements by dropping into WASM." WASM may not significantly outperform the JIT of one particular browser on a given scenario but you are more likely to get homogeneous performance across different browsers.
- pretentious7 5y agohttps://blog.feather.systems/jekyll/update/2021/06/21/WasmPerformance.html https://blog.feather.systems/jekyll/update/2021/06/21/WasmPe... We did that! Yea chrome basically keeps right up, of course not accounting for SIMD
- fulafel 5y agoFor math calculations amenable to SIMD, browser vendors have already designed in a huge advantage for WebAssembly: plans for SIMD support in JS was dropped, and kept for WebAssembly. At the start it gives up to a 4x advantage to WebAssembly with the basic 128-bit SIMD, but possibly 16x later if/when WebAssembly gets support for vector widths corresponding to today's hardware. (anyone know why they started with narrow SIMD? surely it's easier to emulate 512-bit simd with 128-bit simd instructions than try to compile 128-bit simd to take advantage of 512-bit simd?)
- pornel 5y agoThere was a back-and-forth on optimizing this (see the link Steve found). The WASM version got faster too by adopting algorithmic changes proposed here, and the conclusion of Rust authors was that you do need WASM if you want predictable performance. High-perf JS relies on staying on the JIT happy path, not upsetting GC, and all of this is informal trickery that's not easy to do for all JS engines. There's never any guarantee that JS that optimizes perfectly today won't hit a performance cliff in a new JIT implementation tomorrow.
- steveklabnik 5y agohttps://fitzgeraldnick.com/2018/02/26/speed-without-wizardry.html https://fitzgeraldnick.com/2018/02/26/speed-without-wizardry...
- eevilspock 5y agoI know we are not supposed to talk about down voting but in this case I would really like to know why? The article Steve links to is great and even the GP (top comment) approves. Yet Steve's comment is now dimmed. I thought HN was supposed to be better.
- edflsafoiewq 5y agoIt isn't dimmed. It contains only a link and visited links are styled to be lighter gray than normal text. You can check this with the inspector: normal comments have the class c00 (color: #000000); dimmed comments have classes like c5a (color: #5a5a5a), c73, etc.
- eevilspock 5y agook bad. too late to delete.
- steveklabnik 5y agoHave an upvote from me; sorry that my lack of anything but a link contributed to the confusion here!
- recursivedoubts 5y agoIt is shocking how fast javascript is. Hyperscript is an interpreted programming language written on top of javascript with an insane runtime that resolves promises at the expression level to implement async transparency. Head to the playground and select the "Drag" example: https://hyperscript.org/playground/ https://hyperscript.org/playground/ And drag that div around. That whole thing is happening in an interpreted event-driven loop: repeat until event pointerup from document wait for pointermove(pageX, pageY) or pointerup(pageX, pageY) from document add { left: `${pageX - xoff}`, top: `${pageY - yoff}` } end The fact that the performance of this demo isn't absolutely CPU-meltingly bad is dramatic testament to how smoking fast javascript is.
- nemetroid 5y agoI cannot tell if you're being ironic or not. Being able to drag a static text box around is not a very impressive feat. And more importantly, it is CPU-meltingly bad: when I drag the box around, my Firefox process jumps from 5% to a steady 35% (on a Ryzen 2600).
- ohgodplsno 5y agoYou're... dragging an empty div around. You know what else doesn't choke? Me dragging absolutely any window on my PC. It will happily do so at 120Hz without chugging. It even does crazy things, such as playing games with complex event loops at 120FPS. The fact that you're excited at how dragging a single div around actually works as expected is a testament to how horrifyingly low your expectations are.
- ex3ndr 5y agoFor JS it is the same - moving empty div or moving whole window - it is still same amount of code and actual moving is always on different threads. It works for native apps too.
- handrous 5y agoDoesn't work on desktop Safari. Div "rises" on-click and the cursor changes as (evidently) intended, but the div cannot be dragged.
- mamcx 5y agoThis nicely show why Rust/WASM speed up things. All that detective work? Is UNDOING what javascript do, idiomatically! Can be argued that JS is "fast" after all that, but instead, that show Rust give it for free, because is idiomatic there. The problem with JS and other mis-designed languages is that do easy things are easy, but add complexity is easier. And the language NOT HELP if you wanna reduce it. In contrast, in Rust (more thanks to ML-like type system), the code YELL the more complex it becomes. And Rust help to simplify things. That is IMHO the major advantage of a static type system, enriched on the ML family: The types are not there, per-se, as performance trick, but for guide how model the app, then you can model performance! P.D: In other words? If JS were alike rust (ala: ML, plus more use of vectors!) it will be far easier to a) not get on a bad performance spot in first place b) easy to simplify things with type modeling!
- derefr 5y agoI wouldn’t call JS mis-designed. Most use of JS in the world is still for simple things, e.g. validating form inputs to put an X or checkmark beside each field. It’s important for those cases that in JS “easy things are easy”, even at the expense of developer productivity for complex apps. JavaScript (the syntax+semantics) was never intended for complex apps. It would be better if we had JS and another language that were both usable in browsers to interact with the DOM, where this other language was more low-level. And this was the original plan — the “other language” was originally going to be Java. (Java applets were originally capable of manipulating the DOM, through the same API surface as JavaScript!) Then people stopped liking Java applets, so it moved to thinking about “pluggable” language runtimes, loadable through either NPAPI (Netscape/Firefox) or ActiveX (IE), enabling <script type=“foo”> for arbitrary values of foo. This effort died too, both because those plugin systems are security nightmares (PPAPI came too late), and because browsers just didn’t seem willing to standardize on a plugin ecosystem in a way that would allow websites to declare a plugin once that enables the same functionality on all past, present, and future browsers, the way JS does. Eventually, we acknowledged that all browsers had already implemented JavaScript engines (incl. legacy browsers on feature-phones) and so it would be basically impossible to achieve the same reach with a latecomer. So we switched to the strategy of making browsers’ JavaScript engines work well when you use in-band signalling to (effectively) program them in another language; and we called the result WASM. This isn’t the cleanest strategy. What’s great about it, though, is that (the text format of) WASM will load and run in any browser that supports JavaScript itself.
- dboreham 5y agoWhy would someone think that the purpose of WASM is to speed up JavaScript?
- sam0x17 5y agoI read that sentence as replace the word "JavaScript" with "Frontend" and it made more sense. Generally though, I think wasm represents an opportunity to break the strangle-hold JavaScript has had on the frontend ecosystem for nearly 3 decades. Typed, compiled languages typically have huge advantages in terms of safety and performance over dynamically typed interpreted languages. And now a lot of high-level features and syntactic sugar that used to be only available in dynamic languages is becoming available in systems languages.
- fulafel 5y agoSIMD is locked away behind WebAssembly, for many tasks you'll be giving up many multiples of available performance if you stick to JS.
- flohofwoe 5y agoYes, Javascript can be made surprisingly fast, but it requires very detailed knowledge of the JS engine internals and may require giving up most high-level features that define the language (as demonstrated by asm.js). And the resulting carefully optimized JS code is often less readable / maintainable than relatively straightforward C code doing the same thing compiled to WASM.
- sam0x17 5y agoYeah, it strikes me as an odd choice that V8 doesn't just use C code for something as central and important as sorting, but maybe there's some technical reason.
- inbx0 5y agoThere's probably some overhead for switching between a C++ implementation and user-defined JS, and the sorter would need to do that a lot since most sort calls come with the compare-callback.
- derefr 5y agoMy understanding comes from Erlang’s HiPE optimizer (which was AoT rather than a JIT), but I think you’ve nailed it. Most JITs can’t optimize across a native↔interpreted boundary, any more than compilers can optimize across a dynamic-linkage (dlopen(2)) boundary. When all the code is interpreted, you get something that’s a lot more WPO-like. IIRC, some very clever JITs do a waltz here: 1. replace the user’s “syscall” into the runtime, with emitted bytecode; 2. WPO the user’s bytecode together with the generated bytecode; 3. Find any pattern that still resembles the post-optimization form of part of the bytecode equivalent of the call into the runtime, and replace it back with a “syscall” to the runtime, for doing that partial step. Maybe clearer with an example. Imagine there’s a platform-native function sha256(byte[]). The JIT would: 1. Replace the call to sha256(byte[]) with a bytecode loop over the buffer, plus a bunch of logic to actually do a sha256 to a fixed-size register; 2. WPO the code with that bytecode loop embedded; 3. Replace the core of the remnants of the sha256-to-a-fixed-size-register code, with a call to a platform-native sha256(register) function.
- edflsafoiewq 5y agoPreviously https://news.ycombinator.com/item?id=16413917 https://news.ycombinator.com/item?id=16413917
- dcomp 5y agoRegarding caching and memoisation. Isn't the main benefit memory usage saving. I wonder if it's possible to do parsing for speed and then background deduplication for memory savings. (I don't know what the status is of multithreading in js or wasm)
- catlifeonmars 5y agoHuh? Caching is explicitly about extracting time savings at the cost of increased memory usage. Did I read your comment wrong?
- jonnytran 5y agoIs there a 2021 update to this topic? How has the state of WASM changed? How have JS engines changed? Is it still a good idea to make these changes to your JS code?
- pretentious7 5y agohttps://blog.feather.systems/jekyll/update/2021/06/21/WasmPerformance.html https://blog.feather.systems/jekyll/update/2021/06/21/WasmPe... Here's some benchmarks. But in summary, for small simple hot loops, chrome is just as fast using js, but for firefox wasm gives big performance improvements. Of course, if you can use SIMD instructions, then wasm will win, but that's less fair.
- 29athrowaway 5y agoI disagree. JS optimizations are implementation defined. They are also largely undocumented. You will have to read source code if you want to know the truth, or write microbenchmarks to probe the VM behavior. Your optimization from today can be deoptimized tomorrow. There are no documented guarantes that certain code will remain fast. Knowing about inline caching, shapes, smis, deoptimizations and trampolines... does helps. But the internals are unintuitive. Instead, I can save myself that time and use WASM instead. There are so many things that can trigger a deoptimization that I would rather ignore them all and do it in WASM instead.
- dang 5y agoDiscussed at the time: Maybe you don't need Rust and WASM to speed up your JS - https://news.ycombinator.com/item?id=16413917 https://news.ycombinator.com/item?id=16413917 - Feb 2018 (181 comments)
- pretentious7 5y ago(https://blog.feather.systems/jekyll/update/2021/06/21/WasmPerformance.html https://blog.feather.systems/jekyll/update/2021/06/21/WasmPe...) OP was very useful for us in optimizing our js vs wasm benchmarks. We were wondering what a very simple parallel problem like mandelbrot rendering would show about browser jits vs their wasm compilers. Our conclusion was that wasm was consistent across browsers whereas js wasn't. Further, If you can use simd, wasm is faster. Also, v8 is way faster than spidermonkey.
- lovasoa 5y agoYour link gives a 404, I think there is a problem with the path in the URL. And since you are talking about the fastest mandelbrot on the web, here is my contribution https://mandelbrot.ophir.dev https://mandelbrot.ophir.dev It renders in real time and is fully interactive, all in pure js.
- pretentious7 5y agoDoes it still 404? Sorry I made some mistake with it. Theres a link in the post to an interactive demo. The reason I said fastest is cause I wrote the hot loop with SIMD instructions. I'll be adding in period checking and stuff in a couple months time, will be sure to refer your work then, thanks.
- catlifeonmars 5y ago> Maybe you don’t need Rust and WASM to speed up your JS Perhaps, but you do need it in order to have an excuse to use Rust and WASM. True story, this is what I did last weekend.
- Animats 5y agoThat's the problem. Unless you're doing something which does a very large amount of work on the client, you should not need WASM. Game engine, yes. Blog, no. Shopping cart, no. Scrollbar, no. Snooping on the user's mouse cursor, NO.
- catlifeonmars 5y agoAbsolutely. Incidentally, I’m very new to game programming (and performance-critical code in general). Are there high-level GC’d languages which are well suited for (pick some flavor of) game programming? I always read about C++ or (increasingly) Rust when it comes to game programming. Even with all its modern features, Rust still errs on the zero cost abstraction side when it comes to abstracting away the underlying memory and execution model. In 2021, are compilers, JITs, and GC algorithms still worse than humans at optimizing performance critical code? I thought we’d have moved past that by now.
- Animats 5y agoUnity is mostly programmed in C#. It's a good way to start developing games. Unreal Engine and C++ are used for many AAA titles, but it's a harder world than Unity. There are some game engines in Rust for simpler 3D games. I haven't tried them. 2D games seem to be well supported in Rust. I'm working in Rust, writing a client for a virtual world, and I have to say that the 3D graphics ecosystem for that kind of thing in Rust is not quite there yet. I'm using bleeding edge packages (Rend3->WGPU->Vulkan) where I'm in regular contact with the developers. I'm doing it this way mostly because I want to see if I can get better performance than the existing single-thread C++ client, which is compute-bound in the main thread.
- kaba0 5y ago
- devwastaken 5y agoYou need wasm to get any good performance on data intensive applications, because SIMD.