Hacker News new | past | comments | ask | show | jobs | submit login
The Rusty Web: Targeting the Web with Rust (davidmcneil.github.io)
312 points by 314testing on March 8, 2017 | hide | past | favorite | 107 comments



It looks like the slowdown on wasm is related to the powi call in the distance function (which the benchmark page mentions as a likely suspect). Emscripten currently compiles @llvm.powi into a JS Math.pow call, however this powi call is just being used to multiply a number by itself. I filed this issue:

  https://github.com/kripken/emscripten-fastcomp/issues/171
to track this issue in Emscripten.

In JS, the assumption is that code may be written by humans, so engines are expected to optimize things like Math.pow calls with small integer exponents implicitly, which is likely why this code is faster in JS. And in the asm.js case, the page mentions that it's using "almost asm", which is not actually asm.js, so it's using the JS optimizations.

This is one of the characteristic differences between JS and WebAssembly: in WebAssembly, the compiler producing the code has a much bigger optimization role to play.


For some reason, Math.pow implementation in JavaScript is really optimized for special cases of multiplying numbers. I ran into this too when I found this surprising case of faster performance of Go when transpired into JS [1].

[1] https://medium.com/gopherjs/surprises-in-gopherjs-performanc...


> For some reason, Math.pow implementation in JavaScript is really optimized for special cases of multiplying numbers.

It's used for multiplying integers in SunSpider: https://github.com/adobe/chromium/blob/master/chrome/test/da...


Oh yay. Micro-optimisations for benchmarks. That could never go wrong.


Blame the people who continue to hold up SunSpider as a useful benchmark. :P

From https://news.ycombinator.com/item?id=7837433 :

"(For example, did you know that every major JS engine now has a daylight savings offset cache, something which is entirely useless for any real code, but substantially speeds up the date benchmarks in SunSpider? Bleh.)"

From https://blog.mozilla.org/javascript/2013/08/01/staring-at-th... :

"All Javascript engines, including SpiderMonkey, use optimizations like transcendental math caches (a cache for operations such as sine, cosine, tangent, etc.) to improve their SunSpider scores."


> Blame the people who continue to hold up SunSpider as a useful benchmark.

As opposed to some other benchmark, that gets some other micro-optimizations? I'm not sure that's productive. That said...

> "(For example, did you know that every major JS engine now has a daylight savings offset cache, something which is entirely useless for any real code, but substantially speeds up the date benchmarks in SunSpider? Bleh.)"

I'm not sure this is without benefit for me. I store time stamps in UTC in the DB, and pass them along in that form to the client, which then converts to local time. I imagine this might benefit the sometimes hundreds of conversions the client does in my case?

Edit: Maybe what we need a something like the Fortune 500, but for webpages. Take the top 50 web properties(by whatever metric), and allow them to submit benchmark webpages that approximate a real world situation. Facebook would have one that loads a canned sample Facebook account, Google might have one to approximate search results, one to approximate docs, and one to approximate gmail, etc. Only allow new submissions once a month or quarter, but index them so we can see performance over time, performance against older technology, and how technologies change over time.


> As opposed to some other benchmark, that gets some other micro-optimizations?

Yes, because SunSpider in particularly is notoriously, egregiously bad. Google's Octane benchmark suite ( https://developers.google.com/octane/benchmark ) at least attempts to approximate real-world usage by benchmarking actual applications (like PDF.js), as well as scraping regexes from popular sites and using those as part of a regex performance suite. (And even still there are some arguably dubious entries in Octane.)


The whole code is horribly over-engineered, at least for a performance benchmark.

Anybody who uses a pow call to square a number must believe compilers are magic. Worse yet, some compilers might actually optimize for that case, reinforcing that belief.

Compilers should be dumb, fast and predictable instead. That way, everyone gets what they deserve.


Wow; I really disagree with this viewpoint. I want clever compilers that produce good code from high-level specifications. I fully accept the lack of predictability.

Also, your comment that "everyone gets what they deserve" suggests that those who write high-level code (e.g. using pow() to square a number) "deserve" to have slow code. That's a strange moral outlook.


You have to keep in mind that adding support for special cases makes the general case a little bit slower (because it has to always check if it's the special case or not). It's not completely free to make pow work fast on small integers.

So, it's a trade-off. I would also prefer less special cases optimized for naive use, but only if it's accompanied by documentation that says "prefer X instead of pow(x, 2), prefer Y instead of pow(-1, k), etc." It shouldn't be guesswork as to what's best to do for common cases.


That's not necessarily true. I'd wager that most cases where pow(x, 2) is used as a way to square x, the "2" is actually a constant. That's trivially statically optimizable at compile time.


In fact, if you are using pow specifically to "square a number" in the sense where you could replace it with x*x, it is guaranteed to be something you can determine statically (and probably pretty easily) - or you're already doing a lot of unnecessary work.


Most of the time, you can opt out of these optimizations.


I think you're both right. The world of software is sufficiently diverse that there is no single answer to this question that works best for everyone in every situation.


It really isn't. You want something for free without putting the work. I want to put in some work to get what I need.

You, too, believe in the magic compiler that doesn't exist. You don't get good code from a high-level specification. You really have to understand what the compiler actually can do for you and write your code accordingly, either way - except the predictable case is more straightforward (though maybe not as pleasing).


"Putting in the work" often means not being able to use abstractions, which reduces the maintainability of code. That's what people often miss about optimizations: one of their most important uses is to enable abstractions.

As a simple example, take SROA: without that optimization, you can't make a "Point { float x, y, z; }" struct and have that be as efficient as "float x, y, z;", because x, y, and z can live in registers in the latter, while in the former they remain as memory instead of being promoted to SSA values. But being able to use a Point structure like that is extremely helpful, because then I can define useful methods on it and so forth. I shouldn't have to choose between good engineering practice and performance, and thanks to SROA, I don't have to.


You've just invited the assumption that your compiler can do SROA as the basis for better performance - how is that an abstraction?

If you really care about performance to the point of register occupancy, you need to look at the context. The "abstraction" of having a Point type with methods is almost certainly far from optimal, because it doesn't fit SIMD well. It's then also not "good engineering practice" to use it.


> You've just invited the assumption that your compiler can do SROA as the basis for better performance - how is that an abstraction?

"Expands to the exact same code everywhere on every imaginable compiler, even toy compilers nobody uses" is not part of the definition of "abstraction".

> If you really care about performance to the point of register occupancy, you need to look at the context. The "abstraction" of having a Point type with methods is almost certainly far from optimal, because it doesn't fit SIMD well.

What do you think http://www.agner.org/optimize/#vectorclass is then?

> It's then also not "good engineering practice" to use it.

Yes, it is! It makes your code more readable, and if you're compiling on any production-quality C compiler anywhere your Point class will have the same performance as the raw version. Lower maintenance cost, fewer bugs, same performance.


> "Expands to the exact same code everywhere on every imaginable compiler, even toy compilers nobody uses" is not part of the definition of "abstraction".

MSVC compiler doesn't do SROA, as far as I know.

> What do you think http://www.agner.org/optimize/#vectorclass is then?

Have you actually looked at that thing? It's not a Point struct, I can tell you that. There's nothing abstract about it.

If you want to take advantage of SIMD fully, you need to lay out your data in a very specific way. A Point {x,y,z} struct doesn't naturally fit a SIMD register.

Now, if you're willing to make a lot of assumptions on your compiler, you can do something like this: http://www.codersnotes.com/notes/maths-lib-2016/

Still, you need to put in the work and the research. No magic.

> Yes, it is! It makes your code more readable, and if you're compiling on any production-quality C compiler anywhere your Point class will have the same performance as the raw version. Lower maintenance cost, fewer bugs, same performance.

If performance really matters then your abstract solution is almost certainly suboptimal and it's not good engineering practice to use it for the sake of readability.


> MSVC compiler doesn't do SROA, as far as I know.

Yes, it has since at least 2010 (and probably earlier). See "scalar replacement": https://blogs.msdn.microsoft.com/vcblog/2009/11/02/visual-c-...

> Have you actually looked at that thing? It's not a Point struct, I can tell you that. There's nothing abstract about it.

The Vec classes can be used as Point structs.

> If you want to take advantage of SIMD fully, you need to lay out your data in a very specific way. A Point {x,y,z} struct doesn't naturally fit a SIMD register.

So pad it out to 4 fields, using homogeneous coordinates.

> Now, if you're willing to make a lot of assumptions on your compiler, you can do something like this: http://www.codersnotes.com/notes/maths-lib-2016/

That's not making a lot of assumptions about your compiler. The x87 floating point stack, for example, has been obsolete for a long time.

> If performance really matters then your abstract solution is almost certainly suboptimal and it's not good engineering practice to use it for the sake of readability.

I disagree. Let's look at actual examples. "Almost certainly" suboptimal abstractions are not what we've seen in Rust, for example, which leans on abstractions heavily.


> Yes, it has since at least 2010 (and probably earlier).

See my reply to whitequark_, this seems to be loop-specific.

> The Vec classes can be used as Point structs.

Oh, sure. Which one though? How do I abstract this, again?

> So pad it out to 4 fields, using homogeneous coordinates.

In other words, "do something else than what I originally did" and "potentially leave 25% throughput on the table".

I think you're trying to pull a fast one on me.

> That's not making a lot of assumptions about your compiler. The x87 floating point stack, for example, has been obsolete for a long time.

Did you even read beyond the first paragraphs? There's five compiler-specific flags you'll have to get right for any of this to work.

Again, there's no free lunch...

> I disagree. Let's look at actual examples. "Almost certainly" suboptimal abstractions are not what we've seen in Rust, for example, which leans on abstractions heavily.

Could you just scroll up for a moment? We are talking about this because someone wrote the "abstract" version of squaring a number and some optimization didn't kick in, resulting in significantly degraded performance. And that's a trivial example! In a real codebase, you'll have to carefully audit compiler output to see if it does the right thing, potentially requiring you to rewrite code. For Rust, you also have the blessing that there is only one compiler...


> MSVC compiler doesn't do SROA, as far as I know.

In the future, consider verifying your extraordinary claims. Of course it does, and it took me about two minutes to demonstrate that: https://godbolt.org/g/9kT1NP


"As far as I know" implies that I might not know, so that's not an extraordinary claim.

You haven't actually verified SROA. The scalar replacement didn't kick in for the non-inlined method, so this could be a result of loop-specific optimizations. The code is also far from optimal, but that's besides your point of course.

Also note that you're testing an RC of the latest version. There's a feature request for SROA from 2013, so it may be implemented by now: https://connect.microsoft.com/VisualStudio/feedback/details/...


I disagree that compilers should be dumb. A smart compiler can do impressive things like take types into account to add optimizations or it can also take use patterns into account to make Javascript nearly as fast as C++.

With dumb compilers we are putting the need to understand every machine on every developer. Isolating what knowledge is required in different locations is exactly why we have abstractions. The compiler is just one more abstraction to help this kind of specialization and basic division of labor.


And for smart compilers, developers have to have intimate knowledge about the implementation details in order to work around infelicities, or understand exactly how much performance they're leaving on the table when it does or does not do something. So what happened to all that abstraction you were clamoring about, where dumb compilers require "understanding the machine" -- when for the 4th time this week you're staring at the generated code from your "smart compiler", wondering why it's leaving performance on the table? I still write assembly and it isn't because I'm working on a Z80, or for a lack of "very smart compilers" -- I assure you. Sometimes the compiler just can't do what I want; other times it's being too clever for its own good and getting in my way. Most of the time it does a perfectly good job.

The abstraction is fundamentally leaky. As someone who worked on a compiler for years, cost models are important, and benchmarks and perceptions are very important -- but they are often non-intuitive and not totally "free", as TINSTAAFL says.

That said, optimizing something like a self-pow call to a square is probably not overreaching as an optimization or whatever. But there is certainly something to be said about over doing it and before you know it, you've kicked the can down to <person in the hallway who's really good at optimizing on platform X> because they had to do it anyway because only they know why the JVM or whatever behaves that way on a Friday with 17 inner class methods or something and you're really sure it can be faster but you've got a deadline in like a week and holy shit you have 3 other things to do.

As an aside, I think if any compiler gets the "most sane cost model award", it would probably be Chez Scheme, since it is both unbelievably fast, and based entirely around a practical metric: basically no optimization is added if it cannot speed up the compiler itself. Performance should be predictable both in terms of runtime and compile time. Since the compiler is a rather general purpose application -- if the optimization isn't worth it there, it's probably too overly specific, and likely isn't carrying its own weight. After 30 years of following this rule -- Chez is very, very fast.


> developers have to have intimate knowledge about the implementation details in order to work around infelicities

If you presume this, we would still need that knowledge for simple compilers too and the hardware.

Regardless, I disagree with this. Even though the abstractions are leaky, not everything is leaked. For example: Algorithms that have tight loops to do things that fit in cache will do better than those that loop over massive sets. This only leaks the cache size and little else.

You claim to develop compilers, so maybe I am out of my depth. When I read blog posts or watch videos, like those from the latest cppcon. They all boil down to the simple advice "write idiomatic code and don't try to be more clever than the problem requires". Then when that performance isn't good enough profile and optimize. This is working well for me in 3d games. When I see fit to look at my assembly all of it makes good direct sense, and when it doesn't it is extremely fast.

So looking at the previous example leaking the cache size; A slightly smart compiler might insert extra instructions to tell some CPUs how to prefetch whatever is next. A very smart compiler will detect data dependency and rework the loop to operate on a data set that fits in cache (maybe even querying the size of the cache). The alternative is for the dev to know the cache szie at the time the algorithm is written and take it into account there. This is a simple example but shows the problem, why should the dev care about the cache size, when it can be automatically accounted for?

Are there some more nuanced examples where the simple pattern of write idiomatically then profile fails?

I happen to have of those videos[1] running in the background right now about abstractions that cost nothing. I will start it over and pay more attention with your added details in mind. I will also look into Chez Scheme this is the first I have ever heard of it.

[1] CppCon 2016: Serge Guelton “C++ Costless Abstractions: the compiler view" - https://www.youtube.com/watch?v=q0N9Tvf7Bz0&index=108&list=P...


Idiomatic coding is ultimately premised on "following the grain of the compiler", with the language as a front-end abstraction to the compiler's various technologies(type checks, optimizations, etc.) - it can be seen as a way of minimizing bottlenecks that would occur when doing something that the compiler can't optimize well or the language can't express well. And for a lot of application code, that's sufficient. You write simple constructs, they get optimized away, everyone's happy.

However, for a compiler writer, it isn't sufficient. They're in the business of being the bottleneck! As such, they quickly enter a space where they have to set down their own principles for what idiomatic code looks like and how much they allow abstractions to leak upwards, negotiating those principles against the ecosystem of real-world code.

A consequence of this is that a compiler that only has to operate on a single codebase has it easy, because all its optimizations can be customized to the problem domain and target devices, while more generalized compilers have a dynamic optimization problem, where some features help some of the time, and not others. And their clients are all going to be programmers who get themselves into big trouble and then want the compiler to solve the problem.


The Chez Scheme rule is also used by Niklaus Wirth with respect to the actual language design, and it shows in the evolution from Pascal to Modula-2 and Oberon. All have extremely fast compilers, although some implementations expend more effort on optimization than others.


The first paragraph is just nonsense. You, too, suffer from the belief in the magic compiler.

Regarding the second statement: These "smart" optimizations happen before architecture specialization, so that's wrong too. But even if it was true, you now shift the burden to understanding every compiler in order to get the optimization you need.

If people expect that pow(x,2) transforms to x * x, then every compiler would have to implement that. This is a trivial example, but you in reality you always have to structure your code appropriately for some optimization to kick in, what if one of your target compilers needs a different structure?


> If people expect that pow(x,2) transforms to x * x, then every compiler would have to implement that. This is a trivial example, but you in reality you always have to structure your code appropriately for some optimization to kick in, what if one of your target compilers needs a different structure?

Do you get your copy of Hacker's Delight off the bookshelf to look up the magic number instead of writing "x / 3"?

Everyone relies on optimizations to some extent. It's simply infeasible to do otherwise.


I'm not saying compilers shouldn't optimize, they just shouldn't try to be clever. Some things can be expected to be done by most any compiler, many things are extremely compiler-specific and only work under very specific circumstances.


I agree that there is a line beyond which you shouldn't expect compilers to optimize. But "being clever" is responsible for lots of seemingly-simple optimizations. If you want something to blame, blame the C language for making program transformations hard due to unrestricted aliasing and so forth. Don't blame compiler authors for responding to the demands of their users.


Taken to its logical conclusion this is an argument that things should never change and never try to get better.

Clearly that is not what you mean and is not a good thing. Perhaps optimizing compilers put you outside of your comfort zone because you think they force you to learn and keep up to date.

You can do that or you can stick to writing idiomatic code that uses the std library for a given compiler. Doing just this tends to produce extremely good results. I often outdo others near just with this when they are dropping to assembly or doing silly workarounds because they know something is slow. Profiling and timing are required to pick up the rest of the performance, and presuming otherwise is silly. No special knowledge of the machine or compiler is required, just a knowledge of when and how to optimize and when and how to write idiomatically.


> Compilers should be dumb, fast and predictable instead. That way, everyone gets what they deserve.

This is what gave us C, while the rest of the mainframe world was busy using Algol, PL/I, Fortran and Lisp dialects. Where many of the compilers already were capable of doing bounds checking elision.


> Anybody who uses a pow call to square a number must believe compilers are magic.

I'm guessing he thought using a temporary variable would look less pretty, and had no idea of the relative costs of imul and ipow.

> Compilers should be dumb, fast and predictable instead.

I don't care too much about dumb or fast, but they definitely need to be predictable. Even with something as low-level as C++ or Fortran, you often have to profile and disassemble to see if the compiler is doing the optimizations you want (e.g. unrolling loops, inlining functions, propagating constants, using SSE), or if you have to hold its hand. Trying to predict whether something as high-level as GHC will do what you want, or to figure out how to make it do so, is an absolute nightmare.


"Trying to predict whether something as high-level as GHC will do what you want, or to figure out how to make it do so, is an absolute nightmare."

GHC also happens to be one of the slowest compilers you could possibly use. It all goes together.


That is what profilers are for.

One cannot realistic expect that any compiler for a given language behaves the same way.


Yes, but it's nice to be able to predict what your compiler of choice will do, or at least to know that all of the things it might do will have similar performance. When you depend upon certain optimizations, you need to diagnose the compiler's failure to do them. This usually means knowing both how to read assembly, and how to force the compiler to do what you want.

This doesn't matter when any correct program is "good enough," but it matters a whole lot when failure to perform specific optimizations makes a program unacceptably slow.


> Compilers should be dumb, fast and predictable instead. That way, everyone gets what they deserve.

That's a good way to get pummeled in benchmarks. Benchmark scores matter a lot to perception: just look at any HN thread about programming language or browser performance, or any review of browsers in the tech press.

We may not like it, but the benchmark-dominated world is the world we live in.


Unreal Engine's Zen Garden using WebAssembly: https://news.ycombinator.com/item?id=13820633


Couldn't get this to work with Firefox 52, or google-chrome-unstable or chromium on Linux. Even with the various about://flags turned on for webgl 2.0 and wasm


Works fine for me with Firefox 52 on Linux, using nVidia 378.13 drivers.

On Chrome 56 with default flags, the tab immediately crashes upon page load. I've tried enabling the "Experimental WebAssembly" flag, which prevents the crash but the demo fails to load with:

WebAssembly instantiation failed: TypeError: WebAssembly.instantiate is not a function


It works for me on Windows 10, NVidia GPU.


That is pretty neat, any other such examples, perhaps with better graphics?


Interesting how when you read the articles about WASM and ASMJS you see stuff like "1.5-2x peformance loss over native code" but here it's 35-3x.

And this is a computation benchmark, it's not even hitting sand-boxed API overheads.


They didn't document what version of Rust they used, but our support is still fairly preliminary. For example, it wasn't until very recently that we even passed in flags to optimize the output at all. My understanding is that there's still a lot of low-hanging fruit with the integration generally.


I didn't see a link to the source. It would be really cool to see the source code so that other people could validate the work and help point out better implementation options. It's like a scientific paper not disclosing its methods.


The upper right-most link on the page says "source code".


Ah. I missed that. Thanks!


If it's a blog post, it's probably nightly.


That doesn't answer the question of "which nightly". Is this a new post? I don't see a date anywhere.

But regardless, my ultimate point is mostly "hey this stuff is brand new so some stuff not being great is to be expected." The exact details aren't very important.


I don't think the blog post bit is important... all of my blog posts, while not performance related, are on stable Rust.


It's a pretty bad benchmark. I don't think there are any conclusions about WASM, ASM.js, or JavaScript that we can glean from this. All we can get is "this person's translation process is not very good."


All "this person's translation process" seems to be is selecting different compile targets in the official Rust compiler and then comparing the performance of each. Maybe some of those targets' optimisation is less mature than others, but the basic benchmark doesn't seem unreasonable to me.


My pointt was, given even the ASM.js version is slower than the vanilla JS version, it's a pretty good sniff test that the problem is in the Rust compiler and not the browser.


As a sidenote, does anyone know how this emscripten stuff works? I understand how you can compile down to a different language, but is there a shim layer that converts OS level API calls to browser APIs? Or is it lower level than that? Either way, that sounds like a massive undertaking and it blows my mind that any of this is even possible.


Came here to ask exactly this question.

Further, and I don't even know if this question is a meaningful one, but are there any curated lists of libraries known to work with wasm, or do you just compile something and hope for the best? I'm curious if there are any gaming-oriented sound libraries known to work well, and with bindings to Rust? I'm a blind developer with a casual interest in building audio games, a niche gaming genre that's basically what it (har har) sounds like. Unfortunately, much of what is available is Windows-only, and I'm a Linux user. But increasingly over the past few years, the web has struck me as a great platform for building these sorts of games. You don't need a powerful CPU for one thing, and the browser's built-in TTS APIs abstract away one of the biggest platform hurdles. There really isn't a good cross-platform hook into text-to-speech systems, and often you need speech presented in ways that clash with how screen readers work.

Anyhow, I've been wanting to experiment with multi-player gaming, and Rust's new wasm support along with Firefox 52's inclusion seems like a nice bit of synchronicity. If only I could find a good audio library known to work, particularly something with spatialization.

And yes, I do know about the Web Audio API, but was disappointed to learn that its panner nodes don't support more than 2 speakers. That said, I don't know if there are any wasm-compatible libraries that'd work with Rust and support 5.1. I'm also looking for a fun project to use Rust with, and an audio game that uses the strengths of both the web and Rust might be just the thing.

Pardon the diversion, but I'm stupidly excited about using the browser as a cross-platform runtime via a language more sane than JavaScript. :)


I've written a blog post about some (C/C++) libraries I found working well in asm.js/wasm:

http://floooh.github.io/2016/04/09/oryol-3rd-party-code.html

All the C libraries in there should be trivial to integrate with Rust, the C++ libs might be a bit more complicated though.

For audio, I can recommend SoLoud, it's multiplatform and also works well in asm.js/wasm (and doesn't add much size):

http://sol.gfxile.net/soloud/


> are there any curated lists of libraries known to work with wasm

I'm not aware of any specifically, but I do know at least one Rustacean who has been playing with this tweeted something like "I'm surprised but these 80 crates i've been using for advent of code work out of the box with wasm". So we'll see!


Emscripten maintains a collection of ports specifically for using with asm.js/wasm: https://github.com/emscripten-ports

It's a bit spare, and certainly not comprehensive. I've personally gotten ICU and jsoncpp working with minimal effort. Basically if the library has good cross platform support it should work.


There's a bunch of maps or polyfills you can use to communicate with external features. Things like WebGL for example. So it just maps those calls to whatever functions you can provide. Or if your C code uses the file system, you can either pick a virtual file system (for web code) or a real file system (for node applications).

The opposite is also true - you have a html5.h header file you can reference to do DOM/browser manipulations from your native code.

It's pretty fascinating.



"Native WebAssembly support is not available in this browser" Firefox Nightly. Something's wrong with the detection


The binary has version 0xD, the last pre-release version of WebAssembly. Firefox and Chrome are now looking for version 0x1.


Same with Chrome Canary. I suspect the toolchain he's using is a little bit behind the wasm spec.


How does rust deal with numbers in JS given that a u64 is unrepresentable in a native JS number type? Does it simply keep an array of byte values around instead?


wasm != JS, basically.


A few comments are mentioning the implementation is probably dated.

Does anyone have benchmarks that tell a different story?


Not a benchmark, but my 8-bit emulator let's you compare asm.js and wasm performance between browsers and platforms somewhat, and (if compiled natively) also for native x64 or ARM performance:

http://floooh.github.io/virtualkc/

It's implemented in a C-with-classes style C++ though, not Rust.

When you press the 'UI' button, there's a little millisecond timer in the top-right corner which tells you how much time is spent in the 'emulator frame', which is pure asm.js/wasm code which doesn't call out into JS APIs.

You can switch between the asm.js and wasm version through the hamburger-menu-icon at the top-left corner.

I'm seeing the "1.2..1.5x slower than native" performance there.

Some of my other demos are also fairly CPU heavy, for instance the two Bullet-Physics demos here:

http://floooh.github.io/oryol-samples/

These demos have asm.js, wasm and pnacl versions and also have fairly detailed timing information which makes it possible to compare performance to the natively compiled versions.

I do currently see some mysterious performance differences between Mac and Windows though, not sure what's up with that (e.g. my mid-2014 MBP with a 2.8 GHz i5 is giving better performance than the 3.5 GHz i7-4770K in my work PC).


> I do currently see some mysterious performance differences between Mac and Windows though, not sure what's up with that (e.g. my mid-2014 MBP with a 2.8 GHz i5 is giving better performance than the 3.5 GHz i7-4770K in my work PC).

It's worth checking the single thread performance of both your CPU models (see www.cpubenchmark.net), sometimes the higher end models actually have slower single thread performance.


Great demos! What is the UI you're using in the mod and sid players?


Dear Imgui: https://github.com/ocornut/imgui

Best thing since sliced bread if I may say so :)


This is accomplished by compiling Rust to asm.js or WebAssembly.

This seems an overly complex approach. Just because you can do it doesn't mean you should. Most of the advantages of Rust don't apply when you're targeting the Javascript engine of a browser.

Modern Javascript, as a language, isn't that bad. Use the right tool for the job.


I can imaging cases where you'd want code to run in a browser without the unpredictability of garbage collection.

Also, modern Javascript has been the only available tool in browsers, but that doesn't mean it's a good one. asm.js and WebAssembly are making it easier to introduce new, and in many cases, better tools.


If you can, but you don't, could you really?

Knowing nothing of rust development or this particular tool, could a potential benefit be that the outgoing javascript is structured ideologically similar to the rust code that went in? I'm not saying that this would be a primary reason to make this switch but surely this approach isn't without benefit or reason?


Why is rust still not standardized?


It's still growing and changing pretty quickly. Even if it was going to be standardized, it's too early now.


Well they've had several years already


That sounds so good. I don't like javascript at all because it's a one of the worst programming language on our tiny world!

Almost every programming languages is better than javascript. The roots of javascript are for tiny special effects and not for huge well running applications. Typescript and all other frameworks for javascript are just workarounds for a buggy language.

In my opinion the final step is to replace completely javascript with something else!


I disagree. JavaScript is far, far from a perfect language, but if you think it's the worst one out there, you must've not payed attention to it in the past few years. I - as well as most non-self loathing JS hackers - use the ECMAScript 2015 (or later) standard, where the language is relatively feature complete. This allows JS to become a relatively complete OO language should you desire, and it also allows it to act (mostly) functionally. `const` and `let` alone fix many of the ambiguous gripes that people had with JS, and if you truly believe it's still the worst language out there, I encourage you to look at these new developments.


He said it's "one of the worst." And even people who write JavaScript admit that.


I've worked on a JavaScript engine and I'll admit that. The language actively fights optimization. Some of this is due to legacy features, some to inconvenient design decisions like mutable prototype chains. I have tremendous respect for everyone involved in getting JS performance to where it is today.


Perhaps it is. PHP is also typically ragged on for being "one of the worst", yet it runs the majority of the web. The common theme between JavaScript and PHP is that it's possible to write some pretty awful code, but that doesn't make it impossible to write _good_ code in either of them. Do we measure languages by the worst we can create with them, or the best we can create with them?

It's also easy to be "one of the worst" when there are only a handful of languages that see widespread use. You only need to be in the bottom 5 of 15 or so languages, but my point is that that may not imply that it's a bad language.


It's not about good or bad code, Javascript doesn't really encourage either. It's a bad tool, but it's good enough for the job. It happened to be the only tool available, up until recently. It will take forever for another ecosystem to catch up.


you need to learn at least 3 languages that you know exactly what means writing good or bad code with a language. After the time you don't want write code with a poor language. If you have learned a better language.

A Software engineer uses always the best tool and not the worst.


The best(only) tool available may be a bad one


Dart is a alternative


These new features fix nothing, they just add more things that weren't technically needed in the first place.

There is no way to "fix" Javascript without breaking compatibility. It will always suffer from its poor initial design decisions, or it will stop actually being Javascript.


I disagree because of the changes I have in C++ between C++03 and C++11. Compatibility was not broken there, and writing with the new features is a breeze but the option to drop to the old stuff when required still exists (even though it is slow and buggy).

I am not javascript expert, but it seems that the the choices in the newer versions of Javascript are similar to the new changes in C++. No language can make it impossible to prevent bad decisions, but the newer versions of these languages can make good decisions easier.


> Compatibility was not broken there

Rewriting a codebase that had:

  #define static_assert(x) ...
Was "fun" (tm) (C++11 adds a 2-argument keyword, C++17 adds a 1-argument overload that finally undoes the need for the rewrite.)

I'll also note we have vastly different standards as to what it would take to "fix" C++. atoi("9999999999") can still launch nethack (undefined behavior!), and C++11's response is to add a new overload, atoll. C++17 still doesn't have modules (TRs aside), still has a grammar that's so obscene to try and parse that compilers still disagree on some of the finer points, and still thinks "undefined behavior" is a hip new metal band to name drop for cred, rather than a last resort.

This is not to say we shouldn't try to improve the language anyways, but compatibility was and will continue to be broken (hopefully in small ways that generate compile errors), and C++ will continue to suffer from it's initial design, and it will continue to do so for as long as it remains C++.


Fix the static assert should have been trivial, it should have been just a find and replace. Just like any other new keyword in any other new update to a language. The only exception might be if you used string concatenation during macro resolution, if that is the case it shouldn't have worked in the first.

atoi causing undefined is not a real complaint because it has always caused undefined behavior. You can't blame changing that on the standard or the new version of C++ because it was already allowed to change with every execution of the program. Trying to fix non-determinism is a good thing because well formed code should already be avoiding these undefined behaviors.

Why do and others try to complain when you use undefined behavior and are surprised when it changes in undefined ways? You specifically asked to not be able to know the answer.


> Fix the static assert should have been trivial, it should have been just a find and replace.

Merge conflicts are the gift that just keeps on giving. Every refactor on another branch touching the same code is now a conflict. Any branch doing a search and replace slightly differently (say, whitespacing) is now a conflict. Have you ever had to resolve so many of these conflicts that you started accidentally misresolving even the trivial ones? I have. The nontrivial ones can become much harder to code review (e.g. two branches refactored code in the same file, possibly already causing a merge conflict, now complicated by 'spurious' conflicts unrelated to that refactoring.)

Guess how I found out that P4Diff's UI was buggy and could corrupt your local changes? ...a similarly 'trivial' search and replace for logging functions. The static_assert one actually wasn't really that bad ;). Still not 'trivial' though.

> [...] atoi [...] You can't blame changing that on the standard

That wasn't my intent. My intent was to point out broken behavior that the standard decided to double down (by adding atoll) on instead of fixing (by, say, deprecating atoi, like your compiler or programming standard should.) atoi causing UB is still a real complaint - just not a broken compatibility complaint.

> Why do [you] and others try to complain when you use undefined behavior and are surprised when it changes in undefined ways? You specifically asked to not be able to know the answer.

I don't?

You're perhaps thinking of unspecified behavior. Undefined behavior is a damn sight worse than "asking not to know the answer": it's accidentally telling the compiler to generate a broken program that could do anything, for nobody does this intentionally.

On the subject of things I do ask: I spend a lot of time on enabling more warnings as errors, using more static analysis tools, new compilers, etc. to try and find every instance of accidental undefined behavior so we can fix it. I'm specifically asking to know about all instances of undefined behavior so we can fix them to not invoke undefined behavior. If I could get a comprehensive answer to this, I'd give C++ a lot less shit.

But I cannot get a comprehensive answer to this. There will always be something to slip through the cracks. And other programming languages, taunting me with their lack of undefined behavior. Hell, I'd even settle for a comprehensive answer to "where's all the undefined behavior in my code", and settle for my existing debugging loop when dealing with undefined behavior in third party and system libraries. But I can't even have that on C++ codebases.

Why don't you complain about this?


> Merge conflicts are the gift that just keeps on giving.

I deeply sympathize you on this.

I see now what you meant on atoi, I suppose I agree. I prefer boost::lexical_cast or rolling my own when performance matters and type safety is in question.

I was too harsh in my earlier comment and I see I wrote at least some it in anger and misunderstanding. The fact it is accidental is bad, more compiler warnings should be able to be enabled by default in my opinion that catch this kind of non-sense. I agree it shouldn't be possible to be accidental.

I suppose I have had this argument with too many people who see C and C++ as slightly more portable assemblers and grow to rely on specific results of undefined behavior. These people try to defend their right to rely on old and specific undefined behavior. I mistakenly lumped you in with these people incorrectly, please forgive me.


Hey, no worries. Undefined behavior gets me angry too ;).


As someone interested in language design, what design decisions, in your opinion, make JS so terrible? The extremely weak type system?


For me the extremely weak type system is one thing. In general the language is too accepting of bad code. It's kinda like HTML, "if you can guess at what it's supposed to do, do it." Personally, I prefer stricter interpretation over weaker versions.

Another big one for me is "this", which has significantly different semantics than nearly any other OO language out there. I personally find it confusing, and it requires you in many cases to understand the invocation point of a function. I can not reason about all the data related to the scope of the function, in the function, without first taking into consideration all the callers of the function.

To be fair though, JS is not the only language that features these flaws. The biggest problem with it is that I'm not really given a choice to use other languages. Yes, there are transpilers, but even with those I almost always need to use JS at some point for integration with other code.


- Bad Performance - Extremely weak type system - No generic language - Too many abstractions - Double behavior of types - Bad scaling - Extremely bad OOP Implementation (Just 1% of OOP) - the Debugging approach doesn't make any sense - The Community (1000+ Frameworks with almost the same features for what existing standards?????)


> - Bad Performance

It's one of the fastest dynamic languages in existence, considerably faster than e.g. Ruby and Python

> - Extremely weak type system

Compared to what? It's a dynamic language.

> - No generic language - Too many abstractions - Double behavior of types - Bad scaling -

Vague, what does these even mean? Compared to what?

> Extremely bad OOP Implementation (Just 1% of OOP)

Again vague, what is it lacking? Perhaps it just implements the bits people actually use?

> the Debugging approach doesn't make any sense

No idea what this means. Chrome devtools debugger is excellent.

> The Community (1000+ Frameworks with almost the same features for what existing standards?????)

It's a huge community, there're bound to be lots of camps and lots of people trying to crack the same nut different ways. What's wrong with competition? I personally think it's a fantastic community, and find others lacking by comparison.


> It's one of the fastest dynamic languages in existence, considerably faster than e.g. Ruby and Python

I think this is kind of a question of what it means for a language to be fast. The big browser companies have poured a mind-blowing amount of resources into making their JavaScript engines fast. This has made these JavaScript engines faster than other implementations of slow languages that have not had similar resources devoted to performance. But this does not mean "JavaScript is fast" in the sense that the design of JavaScript readily enables good performance, and it doesn't make JavaScript fast relative to actually fast languages.

> Compared to what? It's a dynamic language.

JavaScript's type system is extraordinarily weak even compared to most popular dynamic languages. For example:

  $ python -c "print(1 + '1')"
  Traceback (most recent call last):
    File "<string>", line 1, in <module>
  TypeError: unsupported operand type(s) for +: 'int' and 'str'

  $ ruby -e "puts(1 + '1')"
  -e:1:in `+': String can't be coerced into Fixnum (TypeError)
	from -e:1:in `<main>'

  $ node -e "console.log(1 + '1')"
  11


> I think this is kind of a question of what it means for a language to be fast [...]

It's significantly faster than most other dynamic languages. It's fast as a compilation target to the extent we can run Unreal Engine in the browser. So I consider the original comment I replied to that simply stated 'Bad performance' incorrect or at least lazy/contextless criticism. If you want to reframe, fine, but I'm not going there :)

Accidental string coercion is a valid point. Also hasn't bitten me in ~10 years of building large JS apps. And JS has many advantages over e.g. Python these days that to me vastly overshadow that downside (better support for FP for one).

(And if you really want that type safety you can use TypeScript, just another great thing to come out of the JS community, you know the one that GPP criticised for daring to provide choice).


> It's significantly faster than most other dynamic languages. It's fast as a compilation target to the extent we can run Unreal Engine in the browser. So I consider the original comment I replied to that simply stated 'Bad performance' incorrect or at least lazy/contextless criticism.

The entire point of my comment is that the statement "JavaScript is faster than other dynamic languages" is lazy/contextless praise.

V8 is fast. Rhino is slow. JScript 5 is really slow. They are all JavaScript. V8 is fast because people really wanted it to be fast and did some impressive cutting-edge work to make it happen, not because the language lends itself well to speed. It is a credit to the skill of the people working on the JavaScript engines that JavaScript programmers nowadays can enjoy decent speed, and AFAIK it's not particularly attributable to any specific features of the language.


I might want to read an article about a language that is fast in principle due to its specification. But the question that actually matters to me is: do actual implementations of a language's specification exist, that I can use, that are fast? The answer is yes for JS.

Whatever flaws JS clearly has, speed is not one of them. All the widely used implementations of the language are fast compared to similar languages.

I'm unsure why you think it matters that this speed came about due to hundreds of thousands of hours of work spent on optimizing various compiles and interpreters. I never see anyone saying "Boy SQL sure is fast, but only because so many intelligent people spent their careers making it fast, so it doesn't count."

Remember, this little argument started because someone described JavaScript as slow. That claim is incredibly disingenuous. No, JavaScript's speed isn't directly linked to the language spec but...who cares?


"It's a huge community, there're bound to be lots of camps and lots of people trying to crack the same nut different ways. What's wrong with competition? I personally think it's a fantastic community, and find others lacking by comparison."

Why created the c community a paper for to create a "c" version? (https://en.wikipedia.org/wiki/C_POSIX_library)

One reason was to establish a good standard for all people that you can run your code everywhere without effort.

Our history showed many times is better to create a standard instead of many non-standards because that creates unnecessary complexity on our world. Look at the pipeline concept of unix. More and more companies steal this concept to build real time web applications without having so much state (that is concept is pretty old). Our ancestors created all of the wonderful things like tree, pipes, functional and etc. We are just sometimes too stupid to reuse it.


>> - Extremely weak type system

> Compared to what? It's a dynamic language.

But it's worse than just being a dynamic language. The biggest problem is the implicit type conversions. If it would at least throw runtime errors when there is a type mismatch, instead of silently converting to another type, it would help a lot.


I believe the flaws are well-documented and there isn't much dispute over them, though not all of them are real problems.

I also don't think it's the lack of language features that makes it bad. This is something that is getting addressed.

What really does cause problems is the basic types, the type "system" and the way numbers are treated/represented.


The Important stuff is still unfixed

For instance:

- javascript has just one datatype for numbers, HOW CAN YOU WIRTE A NUMBERS APP WHITH PURE JAVASCRIPT SHIT, WTF?. - Debugging is a hell - No generic language - Slow as hell - Double meaning of many this like undefiend - etc - etc

All what you describe above is syntax sugar.

I have learning 6 programming languages (c, go, javascript, python, java and php)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: