9

I've been Googling quite some time for e.g. 'typeof' and 'performance', but I haven't been able to find a satisfactory answer to the following problem.

I am trying to implement complex numbers for the Transcrypt Python to JavaScript compiler, using local operator overloading. Since we're dealing with a dynamically typed language, it cannot be predicted what type of data will be in a variable.

If I translate x + y to JavaScript, having operator overloading switched on, it will translate e.g. as __add__ (x, y) In order to do the right thing, the __add__ function has to check for both x and y whether they are 'ordinary' JavaScript numbers or if one of them or both of them are of type 'complex', since that requires special operations.

The most obvious way to do that is to test for typeof x == 'number'. However, coming from a C/C++ background, it seems ridiculously inefficient to test for equality with a string with six characters which on top of that first has to be retrieved from memory, only to possible add two integers, which for many processors, once parsed, would be only one instruction.

What amazes me most is that checks like this are advised everywhere around the internet as the normal thing to do. Does anyone know if x == 'number' or possible x === 'number' is somehow cleverly optimized to prevent a full string comparison.

To further clarify the problem, here's my current code for the __add__ operator, using the string comparison.

def __add__ (self, other):
    if __typeof__ (other) == 'number':   # Translates to: if (typeof other == 'number') {
        return complex (self.real + other, self.imag)
    else:   # Other is complex
        return complex (self.real + other.real, self.imag + other.imag)

If not can anyone hint me on a quicker way to distinguish between a number and an arbitrary non-numerical object.

Thanks for the tips. The source is now:

def __sub__ (self, other):
    if __typeof__ (other, 'number'):
        return complex (self.real - other, self.imag)
    else:
        return complex (self.real - other.real, self.imag - other.imag)

translated by:

elif node.func.id == '__typeof__':
    self.emit ('typeof ')
    self.visit (node.args [0])
    self.emit (' === ') # Give JavaScript string interning a chance to avoid char by char comparison
    self.visit (node.args [1])
    return

to:

get __add__ () {return __get__ (this, function (self, other) {
    if (typeof other === 'number') {
        return complex (self.real + other, self.imag);
    }
    else {
        return complex (self.real + other.real, self.imag + other.imag);
    }
});},
taras
  • 6,566
  • 10
  • 39
  • 50
Jacques de Hooge
  • 6,750
  • 2
  • 28
  • 45
  • 2
    It's the thing to do in Javascript *because there is quite literally no other way to do it*. In any case, JS is quite definitely not concerned about optimisations at this level; if you are, you probably should use something else. – Daniel Roseman Aug 11 '16 at 17:02
  • I think you have to use isinstance. It is more pythonic way – luminousmen Aug 11 '16 at 17:02
  • 1
    @Daniel Roseman: I know JS isn't ideal for this kind of stuff, still it's what we're stuck with in the browser. But admitted, it'll never be C++. – Jacques de Hooge Aug 11 '16 at 17:05
  • @caps_lock: I could use isinstance, but it would still have to translate to JS one way or the other – Jacques de Hooge Aug 11 '16 at 17:05
  • Why not just compile c++ to asm.js with existing tools? – John Dvorak Aug 11 '16 at 17:09
  • 1
    asm.js might be a better target but also you should actually measure the performance impact of these checks for your workload first. JS runtimes do all sorts of (multi-stage) optimization so musing about how many instructions something is when your source and target are two high-level languages is not going to get your anything – pvg Aug 11 '16 at 17:13
  • @Jan Dvorak: I'd like to use Python as source language since it is considered simpler than C++ by many. It is possible to compile the Python interpreter and VM from C++ to asm.js as is done in the PyPyJs project. Hower this makes pageloads very slow, as it generates much code. And I'd like good interop with pure JavaScript code. Still coding part of the library in C++ and compile it to asm.js is an idea I'll keep in mind, especially since I am also porting a tiny part of NumPy, to experiment with GPGPU. – Jacques de Hooge Aug 11 '16 at 17:13
  • @Ben: So I should use === rather than == to give optimization a chance? – Jacques de Hooge Aug 11 '16 at 17:15
  • @pvg: Indeed I'll have to do some tests, for now it's only intuition. Maybe some JS interpreters are clever about this. – Jacques de Hooge Aug 11 '16 at 17:16
  • I think the thrust of this question is "how is the string interning system implemented in JavaScript?". I think this will be implementation dependent. Although I would expect the non-coercive equality operator to be faster, I am checking the spec to see if this is guaranteed. – Ben Aston Aug 11 '16 at 17:17
  • @Ben Aston: They will sure be implementation dependent. But in order to be true optimizations, they should reflect in performance. So e.g. doing such a test in an inner loop should tell me something. – Jacques de Hooge Aug 11 '16 at 17:21
  • 2
    Reading the spec, I would expect `===` to be faster for your use case. Everything else will be speculation about the internals of the various engines. Empirical testing will tell you where you stand. Of course things could change at the will of the vendors. http://www.ecma-international.org/ecma-262/7.0/index.html#sec-abstract-equality-comparison – Ben Aston Aug 11 '16 at 17:27
  • @Ben Aston I'll then change \_\_typeof\_\_ (other) == 'number' to \_\_typeof\_\_ (other, 'number'), translating that to typeof other === 'number' rather than typeof other == 'number'. Thanks for this improvement. – Jacques de Hooge Aug 11 '16 at 17:29

1 Answers1

12

That depends on the JavaScript engine. But a typeof obj can only return a fixed set of strings. So a compiler/engine can optimize a typeof obj === 'number' into a test that does not do a string comparison, but uses a more efficient test.

The byte code V8 created for if( typeof obj === 'number' ) will be something like this:

268 S> 0x24110cfe4b0 @   62 : 13 04     LdaImmutableCurrentContextSlot [4]
       0x24110cfe4b2 @   64 : 65 00     TestTypeOf #0
       0x24110cfe4b4 @   66 : 86 16     JumpIfFalse [22] (0x24110cfe4ca @ 88)

So at least v8 does in fact have an own command to test if an object is of a certain type, which is not a string comparison.

I don't know if this is true for the other engines, but it is likely that they do the same thing.

t.niese
  • 39,256
  • 9
  • 74
  • 101