[HN Gopher] TypeScript as fast as Rust: TypeScript++
___________________________________________________________________
TypeScript as fast as Rust: TypeScript++
Author : janpaul123
Score : 149 points
Date : 2022-04-07 17:29 UTC (5 hours ago)
(HTM) web link (zaplib.com)
(TXT) w3m dump (zaplib.com)
| chadcmulligan wrote:
| Years ago I wanted to use Typescript for native development, then
| Swift came along, try Swift if thats what you're after.
| janpaul123 wrote:
| If Apple would truly commit to making Swift applications run
| cross-platform (Windows, Mac, Linux, Android, iPhone, Web/wasm)
| in a performant way, they could completely take over and become
| the next stack for performance-intensive applications.
| cromwellian wrote:
| I wonder if you've looked at AssemblyScript
| https://www.assemblyscript.org/
| janpaul123 wrote:
| Yep, but it doesn't really solve any of the problems that I
| mention in the article, unfortunately.
| huydotnet wrote:
| Sorry for nitpick, but in the TypeScript example, should it be
| for (const vec of vecs)
|
| Instead of: for (const vec in vecs)
| janpaul123 wrote:
| Tnx; fixing!
| dudus wrote:
| I thought WASM was considered slower than JavaScript only for the
| use cases where heavy usage of DOM is necessary. Since that's not
| available through WASM it needs to make a call to a JS function
| to perform the DOM manipulation and that incurs extra overhead.
|
| I think these cases will just never be good for WASM. DOM
| manipulation heavy Apps (which is a good chunk of JS code out
| there) may continue to be in JS and only the CPU intensive tasks
| would be computed in WASM.
|
| Isn't that the best practice for WASM?
| dgb23 wrote:
| This issue is being worked on:
|
| https://hacks.mozilla.org/2019/08/webassembly-interface-type...
| [deleted]
| janpaul123 wrote:
| The overhead of going to JS from Wasm these days is very small,
| and the DOM is relatively slow. So doing DOM operations from
| Wasm is not much different than from JS; the vast majority of
| the time is spent in the actual DOM API calls.
| marcosdumay wrote:
| Add to that the fact that complexity tends to exist in an
| exponential curve, and even though the DOM is relatively
| slow, it is still very fast.
|
| So the number of projects where there is enough DOM
| interaction that WASM will be slow, but not enough that JS is
| still usable is very small.
| jkelleyrtp wrote:
| In http://dioxuslabs.com we share our strings (interned) across
| the JS/Rust boundary and only send primitives back and forth.
| All diffing stays in Rust with bump memory allocators managing
| allocations. The JS side is only there to do DOM manipulation.
| [1] It's about 3-4x faster than React.
|
| I'd say a lot of the ergonomics are there, but not every
| pattern has been translated cleanly from React into Rust yet.
|
| [1]
| https://github.com/DioxusLabs/dioxus/blob/master/packages/in...
| meetups323 wrote:
| Browsers are good enough that if you're careful about it you
| can just use a WebWorker and write standard JS, no need to
| worry with the build/compilation/compatibility headaches adding
| a whole new language to your ecosystem entails. Nice part with
| this is you can even have a fallback routine that runs the
| WebWorker code in the main thread to support really old
| browsers.
|
| I wouldn't recommend dropping to WASM until you've ironed out
| the ideal data structures/algorithms to use, and have spent
| some time in the devtools profiler. Hacking on ideas in JS is
| (IME) much faster than doing the same in a compiled language,
| and the built in profiling in browsers works far better for JS
| than any WASM (again IME). Only after you have an optimized
| algorithm, data structure, and implementation, and still find
| perf isn't where you want it would I recommend rewriting in a
| compiled language -- even then I'd wager having the optimized
| reference implementation around will make this much easier and
| faster than if you tried to start from 0 in the compiled
| language.
|
| Bonus points if you can reuse tests and/or have a debug mode
| that runs both implementations in parallel and throws on any
| differences in output.
| OtomotO wrote:
| But I prefer to write other languages, so a compilation
| target is perfect.
|
| So it's either wasm or something like scala.js or fengari.io
| (lua on the web)
|
| (Worry not, I am paid to write JS and TS these days... But my
| rate increased due to the pain it causes ;))
| rob74 wrote:
| > _Toolchain integration can be daunting. You need to set up Rust
| in development builds, production builds, local testing,
| continuous integration, and so on._
|
| Impressive that someone familiar with the usual JS development
| toolchains can write this and keep a straight face...
| emteycz wrote:
| What's so hard about JS toolchain? yarn
| create next-app --typescript
|
| ... and I am done
| girvo wrote:
| For me it's the same yarn create react-
| app my-app --template typescript
| [deleted]
| [deleted]
| janpaul123 wrote:
| You didn't see my face when I wrote that ;)
| pjmlp wrote:
| Nice work!
|
| Note that Microsoft actually has a compiler from a Typescript
| subset into C++, as part of the MakeCode IoT education project.
| janpaul123 wrote:
| Ah yes; I did see that but forgot to mention it:
| https://makecode.com/language
| simjnd wrote:
| Not sure if you're familiar with or heard about AssemblyScript
| [1]. It sounds like the goal you are trying to achieve, but with
| a saner approach: create a TypeScript-like language that compiles
| to WebAssembly. This means it gives you native access to SIMD,
| and eventually even threads! All while avoiding the overhead of
| learning a new language (well there are still some gotchas since
| it is actually a new language, but very close). No need to think
| about whether you need to optimize X function or manually manage
| memory somewhere, and still get the awesome performance of Wasm.
|
| [1]: https://www.assemblyscript.org/
| wrnr wrote:
| I've tried AS for a while but then just learned Rust. The wasm
| ecosystem is a lot more mature on Rust (probably the best of
| all languages) so I'm glad i'v learned it. AS syntax looks like
| TS, but under the hood it's another language with a lot of open
| problems like testing, sending data back and forth between wasm
| and js. AS supports operator overloading, but only for for
| values of the same time, like Vector * Vector, but not BiVector
| * Vector, and Rust does.
| janpaul123 wrote:
| Yep, but it doesn't really solve any of the problems that I
| mention in the article, unfortunately. The Typescript
| familiarity part is nice, but it doesn't really work well with
| existing code; you still have to set up Wasm-JS communication;
| it doesn't work with existing ArrayBuffers / multiple memories;
| it's garbage collected if you want to use more complex objects;
| etc. In many ways it's better to use Rust (which looks a bit
| like Typescript) than AssemblyScript..
| [deleted]
| [deleted]
| rattray wrote:
| I've never written (or even read) JS code with ArrayBuffers. I
| wonder how much mileage you might get with better education
| and/or libs for using them? And perhaps a highly opinionated
| linter for things like optimal for-loop patterns?
| gigel82 wrote:
| We briefly toyed with the reverse idea; wanted to share some code
| between web and native, and TypeScript / C++ already look very
| similar - what if we invented a subset of TypeScript that can be
| simply translated into C++ (as long as you stick to static use
| and types).
|
| Worked ok for a while but broke down real quick once we brought
| in external npm dependencies.
|
| Now we're just embedding V8 in our native apps and running the TS
| code through that. Not as interesting, but lets us import any
| random npm package (which is both good and bad).
| watermelon0 wrote:
| Are you using V8 heap snapshots to speed up startup time by any
| chance?
|
| IME this can greatly improve cold starts.
| janpaul123 wrote:
| Hah, cool! I agree that for your use case V8 makes more sense
| :)
| janpaul123 wrote:
| Hi! Author here. Really curious what you all think. It's a pretty
| crazy idea but maybe there's something to it! Hacker News is
| always a special place for ideas like this -- you never know what
| you're gonna get ;) -- so I'm looking forward to discussing this
| with y'all!
| mattgreenrocks wrote:
| The TypeScript++ example looks like the type of code that JITs
| typically like. Python has Numba, where users affix a @jit
| annotation to functions to request that. It works pretty well
| in my experience.
|
| However, at this point, I'm mostly of the opinion that langs
| need some notion of mechanical sympathy if they want to be used
| for high-performance. JS engines probably have tens of millions
| of dollars of dev effort poured into them by now, yet they get
| soundly beaten by WASM, which is much younger. The conceptual
| foundation matters a lot.
|
| That said, there's probably a nice space for an ergonomic,
| reasonably fast PL that sits between Rust and JS.
| janpaul123 wrote:
| Good points. I agree that choosing the abstractions wisely is
| key here.
| discreteevent wrote:
| That was the main motivation behind the inventors of dart.
| They got frustrated with all the hacks they had to put into
| V8 just because JavaScript was so mechanically unsympathetic.
| You can hardly blame then because they originally came from a
| background of developing smalltalk and self VMs.
| emteycz wrote:
| Isn't JS much more Smalltalk-ish than Dart? I don't have
| much experience with Dart, but it always seemed like a very
| OOP language to me (and not in the "message passing"
| meaning).
| isaacimagine wrote:
| > JS engines probably have tens of millions of dollars of dev
| effort poured into them by now
|
| And JS engine JIT codegen modules are put to good use
| compiling wasm.
| notriddle wrote:
| > That said, there's probably a nice space for an ergonomic,
| reasonably fast PL that sits between Rust and JS.
|
| Other than not running natively in web browsers, isn't that
| language called Ocaml?
| novocantico wrote:
| The main feedback I have is that I wanted to read this near the
| end of my lunch break, because I've been interested for years
| in the idea that a reasonably-restricted subset of TypeScript
| (who needs all that dynamism) can almost definitely be compiled
| to machine code and be made _super fast_ , faster than V8 -- so
| I was looking for a TLDR in this article and spent a couple
| minutes looking for it and then just reading whole paragraphs
| and still don't really know what you've got here.
| janpaul123 wrote:
| Good feedback; I should add a TL;DR. Basically this is a
| proposal to create a language that sits somewhere between
| Typescript and Rust, and which you can incrementally adopt if
| you already use Typescript.
| mattdeboard wrote:
| dang imagine how much more leisurely you could've read the
| article if you didn't also take some of your lunch break to
| write this comment!
| transitivebs wrote:
| Really interesting article.
|
| I was doing some high-level research on a related question the
| other day: https://transitivebullsh.it/webassembly-
| research-9e231b80e6d...
|
| I know you've responded to a few people on here that
| Assemblyscript doesn't address the main issues you're talking
| about in the article, but honestly it does have a lot of things
| going for it.
|
| I'd love to see a more direct breakdown of this space that
| explains why prior works like assemblyscript, nectarjs, walt,
| etc all fall short, and where an ideal solution would need to
| do better.
|
| ^^ this is the type of thing I'd love to chat about if you're
| interested btw
| janpaul123 wrote:
| Nectarjs is interesting; I wasn't aware of that. How did it
| manage to avoid (for example) a garbage collector without
| making changes to the language?
| pjmlp wrote:
| Love it, you should check the Typescript compiler used by
| MakeCode as well.
|
| Maybe there are some ideas there.
| Syzygies wrote:
| You might want to reconsider the ++ naming convention. Many
| people capable of a critical appraisal of C++ wouldn't want an
| association with C++. The prejudice that your name triggers is
| that you don't understand what others think C++ got wrong, and
| you're likely to be making the same mistakes.
|
| It doesn't look like you're making the same mistakes, but why
| trigger that prejudice?
| tomxor wrote:
| Your example really clicked with me, having written a
| substantial quantity of JS in a "memory managed" way to avoid
| GC, and avoiding memcpy by passing buffers in place of
| pointers.
|
| My initial instinct is that putting such considerations behind
| a preprocessor syntax has the disadvantage of making them more
| opaque and magic, and that I personally would continue to do
| such things more explicitly - which I realise is an ironic
| thing to say about an untyped, interpreted language, it's
| probably just the little pseudo "grey beard" on my shoulder
| which occasionally whispers silly things about "building their
| own computer out of rocks and twigs".
|
| I guess the point is that there's a balance to the cost vs
| benefit: making it more convenient (what you call ergonomics),
| making it more accessible to those that were less likely to
| manually use that technique, and perhaps resulting in an
| average of more performant code; but at the cost of more
| obscurity, and more "magic", perhaps resulting in less educated
| programmers.
|
| I'm probably overthinking it. Ultimately this argument even
| applies to for loops, but it's worth being conscious of that
| cost - it's likely a good idea.
| janpaul123 wrote:
| Yeah I hear you; and even allude to that in some parts of the
| article, like this part:
|
| > Another idea to make Typescript-- a bit less restrictive
| and more ergonomic, would be to use reference-counting of
| objects, and then try to optimize most away using compile-
| time reference counting as pioneered by Lobster. This gives
| most of the advantages of manually managed memory, but makes
| the ownership model much easier to reason about. It is
| however slightly less performant, and more importantly, makes
| performance less predictable, since it becomes more reliant
| on compiler cleverness.
|
| Even Rust can hide actual performance behind abstractions,
| and I've already been bitten by that a few times.. Not sure
| what the right solution is here, but I agree that it's super
| important to think about this when designing a language like
| this.
| triyambakam wrote:
| > having written a substantial quantity of JS in a "memory
| managed" way to avoid GC, and avoiding memcpy by passing
| buffers in place of pointers.
|
| What projects were you working on that required that? It
| sounds interesting
| Jasper_ wrote:
| I've done this before for plenty of tight loop code.
| Nothing irks me than staring a profile and seeing "Major
| GC" multiple times in the middle of a big algorithm.
|
| It's certainly not helped by the latest language fads just
| completely flooring the GC gas pedal; special mention
| should go to for...of creating a brand new object for each
| loop iteration. That's usually the first thing I rip out of
| tight loops.
|
| A bit of care can make this stuff go 3x fast on PC, and
| I've gotten completely unusable experiences on mobile (most
| websites) to be 30-40fps.
| tomxor wrote:
| Mostly a variety of web based science education
| simulations. Usually boiling down to some kind of miniature
| specialist physics engine or procedural animation.
|
| Beyond that, it's more of a personal philosophy than a
| requirement (I could easily have done all those things more
| carelessly, but the result wouldn't be so nice, some
| people's laps would be hotter, and some people would end up
| with a slow or jerky simulation). I like things to be
| efficient, I enjoy finding a balance between "efficient"
| and "minimal". That doesn't mean I avoid GC all over the
| place adding unnecessary complexity and over optimizing, it
| just means being considerate where it matters and
| "idiomatic" where it doesn't - in a physics engine (or any
| kind of posteriori simulation) it matters because it's
| running on an interval and will constantly cause
| perceptible GC hiccups if you don't be considerate with
| memory.
| mc4ndr3 wrote:
| Why not write Rust directly, and target WASM for client side
| environments?
|
| I support efforts to tune existing tech stacks. But there is
| such a more direct path to performance, reliability, etc etc by
| dropping (alt)JS entirely now that anything can compile to
| WASM.
| janpaul123 wrote:
| I agree; that is what we built the Zaplib framework for. But
| the reality is that there are tons and tons of existing JS/TS
| codebases out there, and it'd be nice to have an incremental
| speedup path for them too.
| michael_j_ward wrote:
| In the article
|
| https://zaplib.com/docs/blog_ts++.html#best-of-both-worlds
| [deleted]
| freeqaz wrote:
| Dumb question: Do you mean "Fast" as in speed of execution, or
| "Fast" as in compile times? (Or maybe you mean both? I haven't
| used Rust.)
|
| Might be worth clarifying because I immediately got excited about
| a faster TypeScript compiler!
| johnny22 wrote:
| Rust isn't known for quick compile time.
| janpaul123 wrote:
| Yeah I meant speed of execution. Sorry for the letdown! But
| https://github.com/swc-project/swc is a very fast Typescript
| compiler written in Rust :)
| triyambakam wrote:
| > Various articles have been written, comparing the performance
| of Javascript versus WebAssembly ("Wasm") in the browser.
|
| Nit pick: Every word in this sentence in the article is a link.
| This way of linking multiple related resources is so hard to keep
| track of and navigate. I need to hover over or click each word to
| discover the resource.
___________________________________________________________________
(page generated 2022-04-07 23:00 UTC)