Live data from Hacker News

Porffor: A from-scratch experimental ahead-of-time JS engine

porffor.dev

61–70 of 148 posts

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#61
post #58
post #31

Earlier quoted context omitted.

You’re misunderstanding me, I think. Suppose you have some interface with fields a and c. If your function takes in an object with that interface and operates on the c field, what you want is to be able to do is compile that function to access c at “the address pointed to by the pointer to the object, plus 8” (assuming 64-bit fields). Your CPU supports such addressing directly. Because of structural subtyping, you ca…

> Because of structural subtyping, you can’t do that In practice v8 does exactly what you're saying can't be done, virtually all the time for any hot function. What you mean to say is that typescript type declarations alone don't give you enough information to safely do it during a static compile step. But modern JS engines, that track object maps and dynamically recompile, do what you described.

I mentioned this in my original comment:

> This gives you worse-than-JIT performance on all field access, since JITs can perform dynamic shape analysis.

We're talking about using types to guide static compilation. Dynamic recompilation is moot.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#62

I have thought about doing this and I just can't get around the fact that you can't get much better performance in JS. The best you could probably do is transpile the JS into V8 C++ calls. The really cool optimizations come from compiling TypeScript, or something close to it. You could use types to get enormous gains. Anything without typing gets the default slow JS calls. Interfaces can get reduced to vtables or may…

Dart, maybe, but it lost

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#63

Earlier quoted context omitted.

Yeah... It is unclear to me how not using LLVM is a good thing. You'd inherit millions of man-hours of optimization work, code gen, and general thought process. Is there a technical reason why?

In this case, being self contained will help implementing things like `eval()` and `Function()` since Porffor can self-host. That would be much harder with a LLVM based solution.

[deleted]

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#64
post #61
post #58

Earlier quoted context omitted.

> Because of structural subtyping, you can’t do that In practice v8 does exactly what you're saying can't be done, virtually all the time for any hot function. What you mean to say is that typescript type declarations alone don't give you enough information to safely do it during a static compile step. But modern JS engines, that track object maps and dynamically recompile, do what you described.

I mentioned this in my original comment: > This gives you worse-than-JIT performance on all field access, since JITs can perform dynamic shape analysis. We're talking about using types to guide static compilation. Dynamic recompilation is moot.

Oh, I thought JIT in your comment meant a single compilation. Either way, having TS type guarantees would obviously make optimizing compilers like v8's stronger, right? You seem to be arguing there's no value to it, and I don't follow that.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#65

I have thought about doing this and I just can't get around the fact that you can't get much better performance in JS. The best you could probably do is transpile the JS into V8 C++ calls. The really cool optimizations come from compiling TypeScript, or something close to it. You could use types to get enormous gains. Anything without typing gets the default slow JS calls. Interfaces can get reduced to vtables or may…

Contributor to Porffor here! I actually disagree, there's quite a lot that can be improved in JS during compile time. There's been a lot of work creating static type analysis tools for JS, that can do very very thorough analysis, an example that comes to mind is [TAJS]( https://www.brics.dk/TAJS/ ) although its somewhat old.

> there's quite a lot that can be improved in JS during compile time

I wonder how much performance gain you expect to achieve. For simple CPU-bounded tasks, C/Rust/etc is roughly three times as fast as v8 and Julia, which compiles full scripts and has good type analysis, is about twice as fast. There is not much room left. C/Rust/etc can be much faster with SIMD, multi-threading and fine control of memory layout but an AOT JS compiler might not gain much from these.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#66

Earlier quoted context omitted.

I think the even bigger elephant in the room is that TypeScript's type system is unsound. You can have a function whose parameter type is annotated to be String and there's absolutely no guarantee that every call to that function will pass it a string. This isn't because of `any` either. The type system itself deliberately has holes in it. So any language that uses TypeScript type annotations to generate faster/small…

> I think the even bigger elephant in the room is that TypeScript's type system is unsound. Can you name a single language that is used for high-performance software and whose type system is sound? To speed up the process, note that none of the obvious candidates have sound type systems.

[deleted]

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#67

I have thought about doing this and I just can't get around the fact that you can't get much better performance in JS. The best you could probably do is transpile the JS into V8 C++ calls. The really cool optimizations come from compiling TypeScript, or something close to it. You could use types to get enormous gains. Anything without typing gets the default slow JS calls. Interfaces can get reduced to vtables or may…

You can do inference and only fall back to Dynamic/any when something more specific can't be globally inferred in the program. For an optimization pass this is an option.

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#68
post #49
post #32

Earlier quoted context omitted.

So - I know this in theory, but avoided mentioning it because I couldn’t immediately think of any persuasive examples (whereas subtype polymorphism is a core, widely used, wholly unrestricted property of the language) that didn’t involve casts or any/unknown or other things that people might make excuses for. Do you have any examples off the top of your head?

Here's an example I constructed after reading the TS docs [1] about flow-based type inference and thinking "that can't be right...". It yields no warnings or errors at compile stage but gives runtime error based on a wrong flow-based type inference. The crux of it is that something can be a Bird (with "fly" function) but can also have any other members, like "swim" because of structural typing (flying is the minimum…

This narrowing is probably not the best. I'm not sure why the TS docs suggest this approach. You should really check the type of the key to be safer, though it's still not perfect.

   if (typeof animal.swim === 'function') {....}

Re: Porffor: A from-scratch experimental ahead-of-time JS engine

#70
post #64
post #61

Earlier quoted context omitted.

I mentioned this in my original comment: > This gives you worse-than-JIT performance on all field access, since JITs can perform dynamic shape analysis. We're talking about using types to guide static compilation. Dynamic recompilation is moot.

Oh, I thought JIT in your comment meant a single compilation. Either way, having TS type guarantees would obviously make optimizing compilers like v8's stronger, right? You seem to be arguing there's no value to it, and I don't follow that.

My claim is that the guarantees that TS provides aren't strong enough to help a compiler produce stronger optimizations. Types don't just magically make code faster - there's specific reasons why they can make code faster, and TypeScript's type system wasn't designed around those reasons.

A compiler might be able to wring some things out of it (I'm skeptical about obviouslynotme's suggestions in a cousin comment, but they seem insistent) or suppress some checks if you're happy with a segfault when someone did a cast...but it's just not a type system like, say, C's, which is more rigid and thus gives the compiler more to work with.

Post reply on HN