lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I wrote:
> 1c. Specialize the types early on. E.g. here are the two most
> common candidates (more can be added):
>   lua_Number lightfunc_n_n(lua_State *L, lua_Number a);
>   lua_Number lightfunc_n_nn(lua_State *L, lua_Number a, lua_Number b);
> Type checking can be inlined in luaD_precall and is much faster,
> too. Very few (or no) Lua internals need to be opened up.

Quick followup, because this was easy to check: this variant is
around 2x faster than a regular call to a C closure. Which means
it's still 2.5x slower than an operator. Mainly because of stack
slot copying and some remaining overhead in the code path.

Speeding up the latter can be accomplished with a new VM opcode
for calling intrinsic functions. The CALL opcode can be replaced
on-the-fly when a call-site for a function with an intrinsic
equivalent is found (flagged in the closure). Pretty similar to a
polymorphic inline cache (works for interpreters, too).

Another idea -- a meta mechanism for user extensible operators:

Pass a table with user defined operators (*) to loadstring() etc.
Compile this to OP_[LIGHT]{CONST|UN|BIN}OP, indexing a table of
(light) functions held in the Lua function prototype. I guess
this would be very fast.

Slight complication: for (un)dumping to/from bytecode you need to
pass the same table of user defined operators.

(*) Name, arity, precedence, (light) function, optional type
checks, optional constant folding.

Simplified, hypothetical example:

  local userops = {
    PI =     { arity = 0, op = math.pi }, -- arity 0 are constants
    sin =    { arity = 1, op = math.sin },
    cos =    { arity = 1, op = math.cos },
    ["|"] =  { arity = 2, op = bit.bor },
    ["<<"] = { arity = 2, op = bit.lshift },
    ["//"] = { arity = 2, op = function(a, b) return math.floor(a/b) end },
  } -- These should of course better be light functions.

    local a, b = ...

    print( PI * sin a + cos b )
    print( (a << 11) | (b << 3) | 5 )
    print( "Rici buys", 1.38 // 0.16, "apples." )

  ]], nil, userops)(...)

[Yes, of course this works with strings or any other type, too.]

The operator table could of course be global (but overridable)
for convenience. And it may be chained via __index metamethods,
too. It's even imaginable to define yourself a compile time
"const" keyword which modifies the operator table on the fly
(Forth anyone?).

BTW: This would also solve the annoying 'local sin = math.sin'.