

V8 Crankshaft outperforms DartVM - Pharohbot

Comparing this:<p>http:&#x2F;&#x2F;4.bp.blogspot.com&#x2F;-umhKFVMan2s&#x2F;T0wUSwJ0-eI&#x2F;AAAAAAAAAYA&#x2F;wAVWOlteJSw&#x2F;s1600&#x2F;graph1.png<p>with Dart&#x27;s Performance:<p>https:&#x2F;&#x2F;www.dartlang.org&#x2F;performance<p>It seems that V8 CrankShaft outperforms the DartVM, and that&#x27;s CrankShaft, apparently TurboFan is supposed to outperform CrankShaft too... What do you guys think?
======
spankalee
The V8 that Dart is compared against is always the latest, and includes
Crankshaft which came out 4 years ago.

~~~
Pharohbot
How would you say how V8 Turbofan that uses asm.js(outcome predicted either to
be 2x-6x faster than today's V8) compare to the DartVM?

~~~
mraleph
What do you want to compare it on?

"Using asm.js" is not some magic that just makes _all_ JavaScript faster. For
example all those benchmarks from
[http://dartlang.org/performance](http://dartlang.org/performance) are written
in a normal JavaScript not in what is known as "asm.js".

Now TurboFan does not even really "use asm.js". It respects "use asm"
directive to decide which functions to optimize and enables a few
optimizations on such functions. Other than that it's a completely generic
JavaScript JIT compiler, which does not even performs a complete asm.js module
type verification pass (something that OdinMonkey does).

Furthermore if you try enabling TurboFan on a non-asm.js code then you'll
discover that right now TurobFan is lacking infrastructure for adaptive
optimizations which is an absolute requirement for compiling normal JavaScript
(as opposed to asm.js compliant code which is limted to arithmetic, typed
arrays manipulation and essentially statically typed) into efficient machine
code. It will run this normal JavaScript code - but it will not necessarily
run it fast.

That said I am looking forward for TF with those adaptive optimizations
implemented. It's a bold project with a lot of promised value for V8. Though I
would not expect anything as impressive as "6x over Crankshaft across the
board".

------
mezoni
Dart VM uses a black list for methods that cannot be optimized by VM
intelligence. I cannot say anything about V8. But with optimization in Dart VM
not all so good. Dynamic typing in Javascript is a normal behavior (VM
optimized for that). Dynamic typing in Dart produce a lot of pitfalls (in the
sense of the intelligence of optimization).

~~~
spankalee
This is a very misguided comment.

Dart and JavaScript are both completely dynamically typed. The Dart VM and V8
both use inline caches to remember what types are flowing to what call sites.
Both optimize monomorphic call sites the best and megamorphic call sites the
worst. I know of no "blacklist" in the Dart VM.

~~~
mezoni
>> This is a very misguided comment. >> Dart and JavaScript are both
completely dynamically typed.

V8 VM optimized for that (type dynamics).

Dart VM has a lot problem with performance with this dynamics because Dart VM
is a truly object-oriented VM and each value has its own type (including a
user defined types and a lot types in the Dart SDK, also internal witt hidden
implementation, and all their members).

About "blacklists" and "whitelists".

===================================

Method recognizer and [while/black]list

VM: Improve performance of method recognizer and unify the it with the
intrinsifier.

[https://codereview.chromium.org/468793004/](https://codereview.chromium.org/468793004/)

===================================

[https://github.com/dart-
lang/bleeding_edge/blob/master/dart/...](https://github.com/dart-
lang/bleeding_edge/blob/master/dart/runtime/vm/method_recognizer.cc)

INLINE_WHITE_LIST(SET_IS_ALWAYS_INLINE);

INLINE_BLACK_LIST(SET_IS_NEVER_INLINE);

SET_IS_ALWAYS_INLINE(class_name, function_name, dest, fp)

SET_IS_NEVER_INLINE(class_name, function_name, dest, fp)

===================================

[https://github.com/dart-
lang/bleeding_edge/blob/master/dart/...](https://github.com/dart-
lang/bleeding_edge/blob/master/dart/runtime/vm/method_recognizer.h)

===================================

A lot of (problematic) method in these list. Dart VM code generators and
optimizers is unable to optimize usage of them by the Dart VM intelligence.

Hundreds of methods in these lists.

OTHER_RECOGNIZED_LIST(V) over 100 methods

CORE_LIB_INTRINSIC_LIST(V) over 50 methods

CORE_INTEGER_LIB_INTRINSIC_LIST(V) over 30 methods

MATH_LIB_INTRINSIC_LIST(V) 2 methods

TYPED_DATA_LIB_INTRINSIC_LIST(V) over 30 methods

GRAPH_TYPED_DATA_INTRINSICS_LIST(V) 9 methods

GRAPH_CORE_INTRINSICS_LIST(V) 10 methods

INLINE_WHITE_LIST(V) over 60 methods

INLINE_BLACK_LIST(V) 8 methods

POLYMORPHIC_TARGET_LIST(V) about 20 methods

Most of them cannot be optimized due the wrong architecture (which states that
the type system is unsound).

Most of them cannot be optimized due the other problems in architecture.

P.S.

All mentions about them are hardcoded (added in special lists by humans)
because VM has a problems with performance because it cannot optimize code in
some cases due the fact that the normal running mode in Dart VM is an
(unchecked) production mode.

Normal mode when (types system unsound) means that Dart VM cannot to guarantee
that it has possibility correctly recognize a members (of internally
implemented types) that are critical for performance.

~~~
mraleph
> V8 VM optimized for that (type dynamics). > Dart VM has a lot problem with
> performance with this dynamics

This claim is substantially false. Where are you deriving this "Dart VM has a
lot problem..." from? (feel free to respond in Russian). If you actually knew
how V8 is implemented you would know that all values in the V8 have something
called a _hidden class_ (internally in the sources it's called _map_ ) and V8
optimizes based on identity of those hidden classes. This is not that
different from the Dart - classes are just not _hidden_ in Dart.

> A lot of (problematic) method in these list. Dart VM code generators and
> optimizers is unable to optimize usage of them by the Dart VM intelligence.

You are misunderstanding/misrepresenting why these lists exist. Let's talk
about them one by one.

1\. _INTRINSICS_ Most methods on the _intrinsic_ list don't even have pure-
Dart bodies (so there is nothing to optimized with "Dart VM intelligence") and
they are on the list for two purposes:

\- provide fast hand written assembly implementation, that is more efficient
version written in C++; \- tell optimizing compiler how to lower these
instructions into IR operations.

Without them being on the list there is no way optimizing compiler can
optimize anything - because it would only see a call to some native runtime
function with unknown semantics. How is it supposed to understand that
_TypedList._getFloat64 is actually a pair of a CheckArrayBound and
LoadIndexed<Double>() instructions?

Some of these _intrinsified_ methods do have Dart bodies - but we still
provide a hand-written assembly implementation for them. I think these days
it's limited to methods of the Bigint class. This is the same in any arbitrary
width integer library (e.g. look into GMP sources) - you can write it in C++
but people still write these things in assembly because even C++ can't always
produce the best code for this.

2\. _INLINING_WHITELIST_ These are the methods that that we know are usually
beneficial to inline into the caller's code even if other budget constraints
are preventing it. Inlining is a very hard problem in compiler construction -
it's impossible to always make optimal decisions given compilation time
constraints of the JIT. Even AOT compilers need hints for this, and if you
take a random JIT you will discover that it most likely has a similar
whitelist built in. Essentially this white list is here precisely because we
know that we can optimize this methods very well once they are inlined (which
is complete opposite of "cannot be optimized" that you invoke).

3\. _INLINING_BLACKLIST_ Similar to whitelist it contains functions that we
know are not beneficial to inline. Limited to Bigint class actually now, which
is a very special case as explained above.

4\. _POLYMORPHIC_TARGET_LIST_ this is utility list that improves the structure
of the graph that polymorphic inliner builds (it tells inliner that resulting
code would _not_ benefit from merging different branches of the polymorphic
call even if they share the same target). _None_ of the methods on this list
have an actual Dart body.

To summarize all these lists exist because they encode knowledge that is
impossible for Dart VM to get because those properties are essentially
algorithmically undecidable.

~~~
mezoni
What's wrong with the following code?

[https://github.com/dart-
lang/bleeding_edge/blob/master/dart/...](https://github.com/dart-
lang/bleeding_edge/blob/master/dart/runtime/lib/integers.dart)

===============================

class _IntegerImplementation extends _Num {

    
    
      @inline(INLINE.INTRINSICS)     // <============================
    
      int _bitAndFromInteger(int other) native "Integer_bitAndFromInteger";
    

}

===============================

The same method in CORE_INTEGER_LIB_INTRINSIC_LIST

[https://github.com/dart-
lang/bleeding_edge/blob/master/dart/...](https://github.com/dart-
lang/bleeding_edge/blob/master/dart/runtime/vm/method_recognizer.h)

===============================

V(_IntegerImplementation, _bitAndFromInteger, \ Integer_bitAndFromInteger,
504496713)

===============================

If Dart VM can use "patch class" declarations then why this "@inline" should
be considered as a bad?

Maybe your answer in that the "Dart VM ignored all kind of annotations
(including type annotations) why it should use this approach".

P.S.

This is a not a direct problem of dynamism (ignore annotations even if they
hard coded in source).

Problem in that the Dart VM does not want rely on annotations (dynamism is
better, determine everything at runtime or from magic lists).

~~~
mraleph
> If Dart VM can use "patch class" declarations then why this "@inline" should
> be considered as a bad?

Annotation like that is not enough for intrinsics. You would still need to
provide some way of detecting in the compilation pipeline and intrinsifier
that method you are looking at is indeed `Integer_bitAndFromInteger`. In this
particular case you could look at the name of the native, but that does not
work with those intrinsified methods that have Dart bodies. In those cases it
would have to be @intrinsic("name of the intrinsic").

Right now we can write C++ code like this:

    
    
        switch (recognized_kind()) {
          case MethodRecognizer::kSmth:
          case MethodRecognizer::kSmthElse:
            break;
        }
    

If we start to relying on strings - this code will become less readable (or
alternatively you would have to map strings into C++ enumeration which would
require a list quite list one of the above).

To be honest aesthetically I like annotations, so I would like to use them to
replace these lists, but that does not really solve anything or improve much.
Even more: there is really no fundamental difference between having an
annotation and a list like that, so I don't really understand what bothers you
about these lists.

> (dynamism is better, determine everything at runtime or from magic lists)

Annotation would be no less magic (and not available to the user code
anyways).

~~~
mezoni
>> so I don't really understand what bothers you about these lists.

This approach does not allow me to use annotations (or other way) for
specifying that my own "native" methods in "native extension" are required
some attention.

You use for such attention these lists.

Eg.

I have the following methods (via macro defs) in my "native extension":

=========================

    
    
      UNSAFE_READ_INT(8, int8_t)
      UNSAFE_READ_INT(16, int16_t)
      UNSAFE_READ_INT(32, int32_t)
      UNSAFE_READ_INT(64, int64_t)
      // Skipped
      UNSAFE_READ_FLOAT(32, float)
      UNSAFE_READ_FLOAT(64, double)
      // Skipped
    

=========================

These methods are very fast (in C++) but in fact Dart VM executes them (native
methods) very SLOOOOOOOOOOOWWWWWWWWWWWW...

See my question on stack overflow:

========================= Why native wrapped functions in Dart are such
heavyweight in comparison with “DEFINE NATIVE ENTRY” functions that are very
lightweight?

[http://stackoverflow.com/questions/21363429/why-native-
wrapp...](http://stackoverflow.com/questions/21363429/why-native-wrapped-
functions-in-dart-are-such-heavyweight-in-comparison-with-de)
=========================

\- Dart developers does not like annotation

\- Dart VM does not uses annotation

\- Dart VM does not uses type annotation

\- Dart VM executes custom "native (which in fact are very fast)" methods very
slow

This is what is means for me that Dart VM is not a very well balanced for high
performance.

P.S.

This is why "I bothers you about these lists".

Because they are in some cases the only way to improve performance.

~~~
mraleph
> I have the following methods (via macro defs) in my "native extension":

Can't you just expose the data you are reading as an external typed data array
to the Dart code? That would remove any need for those methods.

> \- Dart developers does not like annotation

I like annotations!

> \- Dart VM executes custom "native (which in fact are very fast)" methods
> very slow

This concern is very valid: it is true that transition between Dart code and
native methods is too heavyweight and as a developer you have no way to fix it
yourself. We had plans to fix it eventually - but they never been very high on
the list of things.

Did you file a bug for the slowness of your native extension?

~~~
mezoni
>> Can't you just expose the data you are reading as an external typed data
array to the Dart code? That would remove any need for those methods.

I work on integrating into Dart ecosysem "foreign functions interface" (ffi).

External typed data is not suitable in many cases:

\- Does not provide physical storage address to use code (binary interop
requires that)

\- I don't need it because it heavyweight if use it with C pointers and
references

\- It has limit in size (specified in Dart VM on max array length)

\- I need malloc and typed data does not help here in any way

\- I access data (by physical address) allocated externally

>> Did you file a bug for the slowness of your native extension?

This is useless. A long time ago we already did not come to a consensus.

~~~
mraleph
> I work on integrating into Dart ecosysem "foreign functions interface"
> (ffi).

Truly efficient FFI has to be part of the VM, you can't implement efficient
FFI from outside.

> \- I access data (by physical address) allocated externally

Do you really mean " _physical_ address" here?

External data is created precisely to efficiently access data allocated
externally, because it allows direct raw access to a given region of memory
(sans bounds checking).

~~~
mezoni
>> Do you really mean "physical address" here?

>> External data is created precisely to efficiently access data allocated
externally, because it allows direct raw access to a given region of memory
(sans bounds checking).

Serach in code "Unsafe." and you understand what means "physical address" as a
base and offset.

Eg.

    
    
      Unsafe.memorySet(base, offset, 0, size);
      Unsafe.memoryAllocate(size);
      Unsafe.writeInt8(base, offset, value);
    

[https://gist.github.com/mezoni/f85b04bf19dab8333abf](https://gist.github.com/mezoni/f85b04bf19dab8333abf)

