lua-users home
lua-l archive

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


Hi,

Mark Hamburg wrote:
> Only between two Lua strings. If one of the strings is coming from C, it
> needs to at least go through strcmp if not being actually converted to a Lua
> string.

In fact when I was profiling Diego's Luasocket, I noticed that one of my
test cases was spending 12% of its time in luaS_newlstr() (due to the
specific way Luasocket checks its userdata arguments). Converting C strings
to Lua strings all the time is pretty expensive.

I think the fastest solution is to register all methods with an upvalue
that holds a reference to the metatable. Storing and retrieving the
metatable to/from the registry is not necessary. And you don't have to
provide a unique name or a unique memory address for all of your userdata
types.

The performance sensitive code path is then:

void *luaL_checkudata_upval(lua_State *L, int ud, int uv, char *msg)
{
  void *u;
  if (lua_getmetatable(L, ud) &&
      lua_rawequal(L, -1, lua_upvalueindex(uv)) &&
      (u = lua_touserdata(L, ud))) {
    lua_pop(L, 1);
    return u;
  }
  luaL_argerror(L, 0, msg); /* Never returns. */
  return NULL;
}

lua_rawequal() is certainly a lot faster than lua_rawget() + lua_tostring() +
strcmp() (the lauxlib string key solution) or just lua_rawget() (the
lightuserdata plus fixed pointer solution). Making the check an inline
function or macro will save a few more cycles.

The standard usage is:

#define foo_checkudata(L) \
  ((foo_t *)luaL_checkudata_upval((L), 1, 1, "foo expected"))

int foo_method(lua_State *L)
{
  foo_t *foo = foo_checkudata(L);
  ...
}


Adding the following to lapi.c would allow for an even faster solution:

void *lua_touserdata_mc(lua_State *L, int indexu, int indexm)
{
  StkId uo = luaA_index(L, indexu);
  StkId mo = luaA_index(L, indexm);
  if (!ttisuserdata(uo) || !ttistable(mo) ||
      uvalue(uo)->metatable != hvalue(mo)) return NULL;
  return rawuvalue(uo) + 1;
}

[untested]

Bye,
     Mike