Live data from Hacker News

The JavaScript Oxidation Compiler

oxc.rs

131–140 of 147 posts

Re: The JavaScript Oxidation Compiler

#131

Earlier quoted context omitted.

TBH I don't know how to do that work. If I'm in the backend it's very easy for me. I can think about allocations, I can think about threading, concurrency, etc, so easily. In browser land I'm probably picking up some confusing framework, I don't have any of the straightforward ways to reason about performance at the language level, etc. Maybe once day we can use wasm or whatever and I can write fast code for the fron…

The work is largely the same. You think about allocations: JS is a garbage collected language and allocations are "cheap" so extremely common. GC is powerful and in most JS engines quite fast but not omniscient and sometimes needs a hand. (Just like reasoning with any GC language.) Of course the easiest intervention to allocations is to remove allocations entirely; just because it is cheap to over-allocate, and the G…

I don't think it's the same tbh. In Rust I can often just `rg '\.clone'` and immediately see wins. Allocations are far easier to track statically. I don't have a good sense for "seeing" allocations when I look at JS, it feels like it's unfair to expect me to have that tbh. As for profilers, yes I could see things like "this code is allocating a lot" but JS hardly feels like a language where it's smooth to then fix that, and again, frameworks are so common that I doubt I'd be in a position to do so. This is really in contrast to systems languages again where I also have profilers but fixing the problem is often trivial.

> You think about threading, concurrency, etc: JS is even a little easier than many backend languages because it is (almost excessively) single-threaded. A lot of concurrency issues cannot exist in current JS designs unless you add in explicit IPC channels to explicitly "named" other threads (Service Workers and Web Workers).

My issue isn't with being able to write concurrent code that has no bugs, my issue is having access to primitives where I have tight control over concurrency and parallelism. The primitives in JS do not provide that control and are often very heavy in and of themselves.

I think it's perhaps worth noting that I am not saying "it's impossible to write fast code for the browser", I'm saying it is not surprising that people who have developed skillsets for optimizing backend code in languages designed to be fast are not in a great position to do the same for a website.

Re: The JavaScript Oxidation Compiler

#132

It always comes as a surprise to me how the same group of people who go out of their way to shave off the last milliseconds or microseconds in their tooling care so little about the performance of the code they ship to browsers. Not to discredit OP's work of course.

TBH I don't know how to do that work. If I'm in the backend it's very easy for me. I can think about allocations, I can think about threading, concurrency, etc, so easily. In browser land I'm probably picking up some confusing framework, I don't have any of the straightforward ways to reason about performance at the language level, etc. Maybe once day we can use wasm or whatever and I can write fast code for the fron…

It's not too diffcut in the browser either. Consider how often you're making copies of your data and try to reduce it. For example:

- for loops over map/filter

- maps over objects

- .sort() over .toSorted()

- mutable over immutable data

- inline over callbacks

- function over const = () => {}

Pretty much, as if you wrote in ES3 (instead of ES5/6)

Re: The JavaScript Oxidation Compiler

#133

Earlier quoted context omitted.

TBH I don't know how to do that work. If I'm in the backend it's very easy for me. I can think about allocations, I can think about threading, concurrency, etc, so easily. In browser land I'm probably picking up some confusing framework, I don't have any of the straightforward ways to reason about performance at the language level, etc. Maybe once day we can use wasm or whatever and I can write fast code for the fron…

It's not too diffcut in the browser either. Consider how often you're making copies of your data and try to reduce it. For example: - for loops over map/filter - maps over objects - .sort() over .toSorted() - mutable over immutable data - inline over callbacks - function over const = () => {} Pretty much, as if you wrote in ES3 (instead of ES5/6)

Yes but it's not really fair to expect me to know how to do that. Just because I know how to do it for backend code, where it's often a lot easier to see those copies, doesn't mean I'm just a negligent asshole for not doing it on the frontend. I don't know how, it's a different skillset.

Re: The JavaScript Oxidation Compiler

#134
post #96

Earlier quoted context omitted.

Would you enjoy writing `rm --yolo file` instead of `rm file` every time?

In this case, "file" is the arg, not --yolo. `rm` without any args returns `` rm: missing operand Try 'rm --help' for more information. ``` `oxfmt` should have done the same and `oxfmt .`, with the desired dir ".", should have been the required usage.

I expect invoking a command-line tool without any arguments to perform the most common action. Displaying the help should only be a fallback if there is no most common action. For example, `git init` modifies the current directory instead of asking you, because that’s what you want to do most of the time.

Re: The JavaScript Oxidation Compiler

#135

Earlier quoted context omitted.

What? The very first page of documentation tells you this. The help screen clearly shows a `--check` argument. This is a formatter and uses the same arguments as many others - in particular Prettier, the most popular formatter in the ecosystem. How were you not expecting this? Did you not bother to read anything before installing and running this command on a sensitive codebase?

I do usually run new tools from somewhere harmless, like ~/tmp, just in case they do something unexpected. But most formatters I'm used to absolutely don't do this. For example, `rustfmt` will read input from stdin if no argument is given. It can traverse modules in a project, but it won't start modifying everything under your CWD. Most unix tools will either wait for some stdin or dump some kind of help when no argu…

a more related tool would be prettier, which also has a --write option

Re: The JavaScript Oxidation Compiler

#136

Earlier quoted context omitted.

It's not too diffcut in the browser either. Consider how often you're making copies of your data and try to reduce it. For example: - for loops over map/filter - maps over objects - .sort() over .toSorted() - mutable over immutable data - inline over callbacks - function over const = () => {} Pretty much, as if you wrote in ES3 (instead of ES5/6)

Yes but it's not really fair to expect me to know how to do that. Just because I know how to do it for backend code, where it's often a lot easier to see those copies, doesn't mean I'm just a negligent asshole for not doing it on the frontend. I don't know how , it's a different skillset.

Nobody expects you to know that, but I'm curious to hear how do you know it for backend code but not frontend code. Have any examples?

Re: The JavaScript Oxidation Compiler

#137

Earlier quoted context omitted.

The work is largely the same. You think about allocations: JS is a garbage collected language and allocations are "cheap" so extremely common. GC is powerful and in most JS engines quite fast but not omniscient and sometimes needs a hand. (Just like reasoning with any GC language.) Of course the easiest intervention to allocations is to remove allocations entirely; just because it is cheap to over-allocate, and the G…

I don't think it's the same tbh. In Rust I can often just `rg '\.clone'` and immediately see wins. Allocations are far easier to track statically. I don't have a good sense for "seeing" allocations when I look at JS, it feels like it's unfair to expect me to have that tbh. As for profilers, yes I could see things like "this code is allocating a lot" but JS hardly feels like a language where it's smooth to then fix th…

> I don't have a good sense for "seeing" allocations when I look at JS, it feels like it's unfair to expect me to have that tbh.

I still think that's a training/familiarity problem more than a language issue? You can just as easily start with `rg \bnew\b` as you can `rg \.clone`. The `new` operator is a useful thing to start with as in both C++ and C#, too. (Even though JS new is technically a different operator than both C++ and C#'s.) After that the JSON syntax is a decent start. Something like `rg {\s*["\.']` and `rg [` are places to start. Curly brackets and square brackets in "data position" are useful in Python and now some of C#, too.

After that the next biggest culprits are common library things like `.filter()` and `.map()` which JS defaults to reified/eager versions for historic reasons. (There are now lazier versions, but migrating to them will take time.) That sort of library allocations knowledge is mostly just enough familiarity with standard library, a need that remains universal in any language.

> JS hardly feels like a language where it's smooth to then fix that

Again, perhaps this is just a familiarity issue, but having done plenty of both, at the end of the day I still see this process as the same: move allocations out of tight loops, use object pools if necessary, examine the O-Notation/Omega-Notation of an algorithm for its space requirements and evaluate alternatives with better mean or worst cases, etc. It mostly doesn't matter what language I'm working in the basics and fundamentals are the same. Everything is as "smooth" as you feel comfortable refactoring code or switching to alternate algorithm implementations.

> frameworks are so common that I doubt I'd be in a position to do so

Do you treat all your backend library dependencies as black boxes as well?

Even if that is the case and you want to avoid profiling your framework dependencies themselves and simply hope someone else is doing that, there's still so much in your control.

I find JS is one of the few languages where you can somewhat transparently profile even all of your dependencies. Most JS dependencies are distributed as JS source and you generally don't have missing symbol files or pre-compiled binary bricks that are inscrutable to inspection. (WASM is changing that, for the worse, but so far there are very few WASM-only frameworks and most of them have other debugging and profiling tools.)

I can choose which frameworks to use based on how their profiler results look. (I can tell you that I don't particularly like Angular and one of the reasons why is I've caught it with truly abysmal profiles more than once, where I could prove the allocations or the CPU clock time were entirely framework code and not my app's business logic.)

I've used profilers to guide building my own "frameworks" and help proven "Vanilla" approaches to other developers over frameworks in use.

> The primitives in JS do not provide that control and are often very heavy in and of themselves.

Maybe I'm missing what primitives you are looking for. async/await is about the same primitive in JS and Rust and there are very similar higher-level tools on top of them. There's no concurrency/parallelism primitives today in JS because there is no allowed concurrency or parallelism. There are task scheduling primitives somewhat unique to JS for doing things like "fan out" akin to parallelism but relying on cooperative (single) threading. Examples include `requestAnimationFrame` and `requestIdleCallback` (for "this can wait until you next need to draw a frame, including if you need to drop frames" and "this can wait until things are idle" respectively).

> I'm saying it is not surprising that people who have developed skillsets for optimizing backend code in languages designed to be fast are not in a great position to do the same for a website.

I think I'm saying that it is surprising to me that people who have developed skillsets for optimizing backend code in languages designed to be fast seem to struggle applying the same skills to a language with simpler/"slower" mechanics, but also on average much higher transparency into dependencies (fuller top-to-bottom stack traces and metrics in profiles).

To be fair, I get the impulse to want to leave it as someone else's problem. But as a full stack developer who has done performance work in at least a half dozen languages, I feel like if you can profile and performance tune Rust you should be able to profile and performance tune JS. But maybe I've seen "too much of the Matrix" and my "it's all the same" comes from a deep generalist background that is hard for a specialist to appreciate.

Re: The JavaScript Oxidation Compiler

#138

Earlier quoted context omitted.

Yes but it's not really fair to expect me to know how to do that. Just because I know how to do it for backend code, where it's often a lot easier to see those copies, doesn't mean I'm just a negligent asshole for not doing it on the frontend. I don't know how , it's a different skillset.

Nobody expects you to know that, but I'm curious to hear how do you know it for backend code but not frontend code. Have any examples?

The parent commenter earlier seems to be implying that it's only a matter of not caring.

> care so little about the performance of the code they ship to browsers.

> but I'm curious to hear how do you know it for backend code but not frontend code.

Because I find backend languages extremely easy to reason about for performance. It seems to me that when I write in a language like rust I can largely "grep for allocations". I find that hard to see in javascript etc. This is doubly the case because frontend code seems to be extremely framework heavy and abstract, so it makes it very hard to reason about performance just by reading the code.

Re: The JavaScript Oxidation Compiler

#139

Earlier quoted context omitted.

I don't think it's the same tbh. In Rust I can often just `rg '\.clone'` and immediately see wins. Allocations are far easier to track statically. I don't have a good sense for "seeing" allocations when I look at JS, it feels like it's unfair to expect me to have that tbh. As for profilers, yes I could see things like "this code is allocating a lot" but JS hardly feels like a language where it's smooth to then fix th…

> I don't have a good sense for "seeing" allocations when I look at JS, it feels like it's unfair to expect me to have that tbh. I still think that's a training/familiarity problem more than a language issue? You can just as easily start with `rg \bnew\b` as you can `rg \.clone`. The `new` operator is a useful thing to start with as in both C++ and C#, too. (Even though JS new is technically a different operator than…

> I still think that's a training/familiarity problem more than a language issue?

But that's fine. Even if we say it's a familiarity problem, that's fine. I'm only saying that it's not reasonable to expect my skills in optimizing backend code to somehow transfer. Obviously many things are the same - reducing allocation, improving algorithmic performance, etc. But that looks very different when you go from the backend to the frontend because the languages can look very different.

> You can just as easily start with `rg \bnew\b` as you can `rg \.clone`.

That's not true though. In Rust you have to have a clone somewhere if you're allocating on the heap, or one of the pointer types like `new`. If I pass a struct around it's either cheaply moveable (ie: Copy) or I have to `clone` it. Granted, many APIs will clone "invisibly" within them, but I can always grep to find the clone.

In Javascript, things seem to allocate by default. A new object allocates. A closure allocates. Things are very implicit, you sort of are in an "allocates by default" mode with js, it seems. In Rust I can just do `[u8; n]` or whatever if I want to, I can just do `let x = "foo"` for a static string, or `let y = 5;` etc. I don't really have to question the memory layout much.

Regardless, you can just learn those rules, of course, but you have to learn them. It seems much easier to "trip onto" an allocation, so to speak, in js.

> Again, perhaps this is just a familiarity issue

I largely agree, though I think that js does a lot more allocation in its natural syntax.

> Do you treat all your backend library dependencies as black boxes as well?

No, but I don't really use frameworks in backend languages much. The heaviest dependency I use is almost always the HTTP library, which is reliably quite optimized. Frameworks impose patterns on how code is structured, which, to me, makes it much harder to reason about performance. I now have to learn the details of the framework. Perhaps the only thing close to this in Rust would be tokio.

> I've used profilers to guide building my own "frameworks" and help proven "Vanilla" approaches to other developers over frameworks in use.

I suspect that this is merely an issue of my own biased experience where I have inherited codebases with javascript that are already using frameworks.

> Maybe I'm missing what primitives you are looking for. async/await is about the same primitive in JS and Rust and there are very similar higher-level tools on top of them.

I mean, stack allocation feels like a pretty obvious one, reasoning about mutability, control over locking, the ability to `join` two futures or manage their polling myself, access to operating system threads, access to atomics, access to mutexes, access to pointers, etc. These just aren't available in javascript. async/await in js is only superficially similar to Rust.

I mean, a simple example is that I recently switched to CompactString and foldhash in Rust for a significant optimization. I used Arc to avoid expensive `.clone` calls. I preallocated vectors and reused them, I moved other work to threads, etc. I feel really comfy doing this in Rust where all of this is sort of just... first class? Like, it's not "weird" rust to do any of this. I don't have to really avoid much in the language, it's not like js where I'd have to be like "Okay, I can't write {a: 5} here because it would allocate" or something. I feel like that shouldn't be too contentious? Surely one must learn how to avoid much of javascript if they want to learn how to avoid allocations.

> To be fair, I get the impulse to want to leave it as someone else's problem.

I just reject that framing. People focus on what they focus on. Optimizing their website is not necessarily their interest.

> I feel like if you can profile and performance tune Rust you should be able to profile and performance tune JS.

I probably could but it's definitely not going to feel like second nature to me and I suspect I'd really feel like I'm fighting the language. I mean, seriously, I'd be curious, how do you deal with the fact that you can't stack allocate? I can spawn a thread in Rust and share a pointer back to the parent stack, that just seems very hard to do in javascript if not outright impossible?

> I think I'm saying that it is surprising to me that people who have developed skillsets for optimizing backend code in languages designed to be fast seem to struggle applying the same skills to a language with simpler/"slower" mechanics

Yeah I don't really see it tbh. I mean even if you say "I can do it", that's great, but how is it surprising?

Re: The JavaScript Oxidation Compiler

#140
post #91

Earlier quoted context omitted.

a new user should not expected to know whether to use "--info", "--help", or "-info" or "/info" A power user can just pass the right params. Besides, it is not that hard to support "--yolo" parameter for that use case

I know feels aren't the objective truth but I feel like most people would default to running "new-cli-tool --help" first thing as a learned (defensive) habit. After all quite a bit of stuff that runs in a terminal emulator does something when ran without arguments or flags.

I feel most people should refer to manual before running arbitary command. but that's because "crontab -r" has taught me this the hard way.

new devs should not learn these things the hard way

Post reply on HN