Metatables for primitives performance?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Metatables for primitives performance?

Paul Chiusano-3
I was playing around with primitive metatables. The following works in 5.1:

local function factorial(n)
  return n < 3 and n or n*factorial(n-1)
end
Number = { factorial = factorial }
debug.setmetatable(3, {__index=Number})

Now I can do (7):factorial() and voila! returns 5040! And it seems
that I can pass any number I want into debug.setmetatable, with the
same effect. Pretty snazzy.

My question is: is there a performance penalty associated with doing
this? Does adding this metatable cause all numeric operations to
perform slower? Also, I notice that overriding any of the operator
metamethods (like __div) for numbers has no effect. Is this for
performance reasons?

If you could override the operators for primitives, seems like you'd
need rawdiv, rawadd, and so on. For example:

Number = {
  __div = function(num, denom)
    return denom ~= 0 and (num / denom) or error("Divide by zero!")
  end
}

num / denom would just call the same metamethod, leading to an infinite loop.

-Paul


Reply | Threaded
Open this post in threaded view
|

Re: Metatables for primitives performance?

Petite Abeille

On Mar 24, 2006, at 18:41, Paul Chiusano wrote:

Now I can do (7):factorial() and voila! returns 5040! And it seems
that I can pass any number I want into debug.setmetatable, with the
same effect. Pretty snazzy.

Very cool :)

More madness:

local number = {}

setmetatable( number, { __index = math } )

function number:toDate( aFormat )
        return os.date( aFormat or "!%a, %d %b %Y %H:%M:%S GMT", self )
end

function number:days()
        return self * 60 * 60 * 24
end

function number:ago()
        return os.time() - self
end

debug.setmetatable( 1, { __index = number } )

print( ( 5 ):days():ago():toDate() )

> Thu, 23 Mar 2006 06:41:12 GMT

My question is: is there a performance penalty associated with doing
this? Does adding this metatable cause all numeric operations to
perform slower?

I was wondering about this as well. Furthermore, why isn't the math package the default metatable for numbers in the same way as the string package is for strings?

There must be something very obvious, as after asking the same question several time, no one has bothered answering one way or another. Oh, well... let me shoot myself in the foot 8^)

Cheers

--
PA, Onnay Equitursay
http://alt.textdrive.com/


Reply | Threaded
Open this post in threaded view
|

Re: Metatables for primitives performance?

Mike Pall-3-2
In reply to this post by Paul Chiusano-3
Hi,

Paul Chiusano wrote:
> My question is: is there a performance penalty associated with doing
> this?

It doesn't affect other primitive operations on numbers, so there
is no performance penalty for _them_.

But you make the core do some extra work to just call a function.
This is of course slower than directly calling it.

> Does adding this metatable cause all numeric operations to
> perform slower?

No.

> Also, I notice that overriding any of the operator
> metamethods (like __div) for numbers has no effect. Is this for
> performance reasons?

Yes, very much so. Requiring a check for every primitive
operation on numbers would slow Lua to a crawl.

PA wrote:
> I was wondering about this as well. Furthermore, why isn't the math 
> package the default metatable for numbers in the same way as the string
> package is for strings?

Because it doesn't pay off. It's slower than calling the
functions directly. And it doesn't help to improve the clarity of
the code (x:sin():sqrt() vs. sqrt(sin(x))). I guess OO syntax for
math functions never caught on.

The performance caveat applies to the use of string metamethods,
too. But other languages provide an OO syntax for string ops and
some people like the syntax.

$ time lua -e 'local s=""; for i=1,1e7 do local y=s:len() end'
3.46
$ time lua -e 'local s,len="",string.len; for i=1,1e7 do local y=len(s) end'
2.51
$ time lua -e 'local s=""; for i=1,1e7 do local y=#s end'
0.82

[Lower numbers are better.]

Bye,
     Mike

Reply | Threaded
Open this post in threaded view
|

Re: Metatables for primitives performance?

Paul Chiusano
> > Also, I notice that overriding any of the operator
> > metamethods (like __div) for numbers has no effect. Is this for
> > performance reasons?
>
> Yes, very much so. Requiring a check for every primitive
> operation on numbers would slow Lua to a crawl.

Makes perfect sense. Thanks.

-Paul