Lua 整数类型

发布于 2024-10-08 08:12:53 字数 2656 浏览 4 评论 0原文

我真的需要 Lua 中有一个整数类型。

我所说的整数类型是指定义常用运算符(/ * + 等)并表现得像整数的类型,内部表示并不重要。

用表做这样的事情非常简单,问题是,我尝试过,并且性能非常差(当然)。这是我的部分实现:

function num_op(a, b, calc_func)
    local atype = pytype(a)
    local btype = pytype(b)
    local a_val, b_val

    a_val = (atype == "Integer" or atype == "Double") and a[1] or a
    b_val = (btype == "Integer" or btype == "Double") and b[1] or b
    val = calc_func(a_val, b_val)

    if atype == "Integer" and btype == "Integer" then
        return Integer:create(val)
    else
        return Double:create(val)
    end
end

numeric_mt = { 
    __add = function(a, b)
        return num_op(a, b, function(a,b) return a + b end)
    end,

    __sub = function(a, b)
        return num_op(a, b, function(a,b) return a - b end)
    end,

    __div = function(a, b)
        return num_op(a, b, function(a,b) return a / b end)
    end,

    __mul = function(a, b)
        return num_op(a, b, function(a,b) return a * b end)
    end,

    __tostring = function(a)
        return tostring(a[1])
    end
}

-----------------------------
-- Integer type definition --
-----------------------------

Integer = {}
Integer_mt = table.copy(numeric_mt)
Integer_mt["__index"] = Integer

function Integer:create(value)
    local new_inst = {math.floor(value)}
    setmetatable(new_inst, Integer_mt)
    return new_inst
end

function Integer:className()
    return "Integer"
end

我收集到的主要性能损失是(当然)大量的分配。 LuaJit 能够很好地优化运算符函数,但不能优化元表分配。

有人认为使用自定义 c 实现和 userdata 可以做得更好吗?或者我所追求的东西是不可能实现的?

注意:我知道 lua 没有整数。我也知道我可以使用数学库获得相同的结果。我想要的是使用整数时完全透明度,除了创建阶段。

编辑:我将在此处添加额外的信息,以便一切仍然集中

@Mud:我需要在某种程度上以与 python/ruby/ 中相同的方式进行透明混合算术等,但具有尽可能最佳的性能。我使用 luaJIT 作为编译器的目标,并使用常规 Lua 作为 luaJIT 不支持的平台的后备。这对于性能特征非常重要。

这意味着我希望能够做到这一点:

a = int(5) -- Integer value
b = int(2) -- Another Integer
c = 2      -- Double
d = a / b  -- == 2 , integer arithmetics
e = a / c  -- == 2.5, floating point arithmetics

通过上面所示的实现,我可以达到某个程度。问题是我正在减慢对每个数字的操作,因为常规数字也被装箱。我可以使用调试库重载数字元表,但

  • 我不知道此功能在生产质量软件中使用有多可靠
  • 它仍然会降低数字的性能,因为能够拥有统一的数字接口,我将不得不使用 (number):get(),这在任何情况下都会减慢操作速度。

昨晚我用 C 语言实现了自己的 Integer 实现。问题是,虽然它比我在常规 lua 中的天真实现有了改进,并且也比对 math.floor 的内联调用有了改进,但在使用 LuaJIT 时就不太清楚了,其中内联调用仍然很多 比 C 实现更快。

另一种解决方案是始终使用未装箱的数字,并在编译器中使用某种类型传播来跟踪整数,并在需要时对它们使用适当的内联操作,但该解决方案的复杂性要大得多,并且有点破坏了整体使用 Lua/LuaJIT 作为后端的目的。

我将尝试你的实现,但我怀疑它会比 LuaJIT 中的内联调用更好。我所追求的目标(同时具有双精度和整数的透明操作,以及接近 luaJIT 上的内联调用的性能)很可能是完全不可能的。非常感谢您的帮助。

@miky:谢谢,这看起来不错,但我怀疑我可以用它来修补 luaJIT,如果我不能,它就会失去我的目标的所有兴趣。

i really need to have an integer type in Lua.

What i mean by integer type is a type defining the usual operators (/ * + etc) and behaving like an integer, the internal representation doesn't matter.

Doing such a thing with tables is very simple, the problem is, i tried that, and the performance is terribly poor (of course). Here is my partial implementation :

function num_op(a, b, calc_func)
    local atype = pytype(a)
    local btype = pytype(b)
    local a_val, b_val

    a_val = (atype == "Integer" or atype == "Double") and a[1] or a
    b_val = (btype == "Integer" or btype == "Double") and b[1] or b
    val = calc_func(a_val, b_val)

    if atype == "Integer" and btype == "Integer" then
        return Integer:create(val)
    else
        return Double:create(val)
    end
end

numeric_mt = { 
    __add = function(a, b)
        return num_op(a, b, function(a,b) return a + b end)
    end,

    __sub = function(a, b)
        return num_op(a, b, function(a,b) return a - b end)
    end,

    __div = function(a, b)
        return num_op(a, b, function(a,b) return a / b end)
    end,

    __mul = function(a, b)
        return num_op(a, b, function(a,b) return a * b end)
    end,

    __tostring = function(a)
        return tostring(a[1])
    end
}

-----------------------------
-- Integer type definition --
-----------------------------

Integer = {}
Integer_mt = table.copy(numeric_mt)
Integer_mt["__index"] = Integer

function Integer:create(value)
    local new_inst = {math.floor(value)}
    setmetatable(new_inst, Integer_mt)
    return new_inst
end

function Integer:className()
    return "Integer"
end

The main performance penalty from what i gather is (of course) the very numerous allocations. LuaJit is able to optimize the operators functions quite well, but not the metatables allocations.

Do anybody think it would be possible to do better with a custom c implementation and userdata ? Or is what i'm pursuing impossible to attain ?

NB : i know lua doesn't have integers. I also know that i can obtain the same results using the math lib. What i want is complete transparency when using integers, except for the creation phase.

EDIT : I'm gonna add additional information in here so that everything is still centralized

@Mud: I need, to a certain degree to have transparent mixed arithmetics in the same way you have in python/ruby/etc, but with the best performance possible. I'm using luaJIT as a target for a compiler, with regular Lua as a fallback for platforms not supported by luaJIT. This is very important for the performance characteristics.

It means that i would like to be able to do this:

a = int(5) -- Integer value
b = int(2) -- Another Integer
c = 2      -- Double
d = a / b  -- == 2 , integer arithmetics
e = a / c  -- == 2.5, floating point arithmetics

I can reach that to a certain point, with the implementation showed above. The problem is that i'm slowing operations on every numbers, since regular numbers are boxed too. I could overload the metatable of numbers with the debug lib, but

  • I don't know how reliable this feature is for use in production quality software
  • It will still slow down the performance of numbers since, to be able to have an unified interface to numbers, i'll have to use (number):get(), which will slow down operation in any case.

I rolled my own Integer implementation in C last night. The thing is, although it is an improvement over my naive implementation in regular lua, and also and improvement over inline calls to math.floor, it's much less clear when using LuaJIT, where inline calls are still a lot faster than the C implementation.

Another solution would be to always use unboxed numbers, and use some kind of type propagation in my compiler to track integers and use proper inline operations on them when needed, but the complexity of this solution is a lot bigger, and kind of defeats the whole purpose of using Lua/LuaJIT as a backend.

I'm gonna try your implementation, but i doubt it will be better than inline calls in LuaJIT. It may very well be that what i'm shooting for (having both transparent operation of double and integers, and performance close to inline calls on luaJIT) is plain impossible. Thank you very much for your help.

@miky : Thanks, this is looking nice, but i doubt i can patch luaJIT with it, and if i can't, it looses all its interrest for my goal.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浮世清欢 2024-10-15 08:12:53

为什么你需要它们?帮助您找到问题的高效解决方案的最佳方法是了解问题。您具体需要整数做什么?

我收集到的主要性能损失(当然)是大量的分配。

好吧,你在每个操作上创建闭包,而且我根本不明白为什么你有一个 Double 类,因为 Lua 的数字类型已经是 double 了。你就不能做这样的事吗?

Integer = {}
local function val(o) return type(o) == 'number' and o or o[1] end
function Integer.__add(a,b) return Integer:create(val(a) + val(b)) end
function Integer.__sub(a,b) return Integer:create(val(a) - val(b)) end
function Integer.__div(a,b) return Integer:create(val(a) / val(b)) end
function Integer.__mul(a,b) return Integer:create(val(a) * val(b)) end
function Integer:__tostring() return tostring(self[1]) end
function Integer:create(value)
   return setmetatable({math.floor(value)}, Integer)
end


-- test
a = Integer:create(15.34)
b = Integer:create(775.34433)
print((a*10/2+b-3)/3*a+b) --> 5005

有人认为使用自定义 C 实现和用户数据可以做得更好吗?

是的,C 实现应该更快,因为您不需要为每个 Integer 创建一个表;您的用户数据实际上可能只是一个int*。这也将消除对floor调用的需要。

编辑:我编写了一个 测试 C 实现,它比本文中介绍的 Lua 实现快约 5 倍。

编辑2:到C代码的链接已经死了,所以这是我在硬盘中发现的一些代码,它们可能是也可能不是该链接处的代码。我知道它是针对 Lua 5.1 编译的,但除此之外......不知道它是否是技术上有效的实现。自从我写任何 Lua C 代码以来已经太久了。

#include <math.h>
#include "lua.h"
#include "lauxlib.h"

/* get value of Integer userdata or Lua number at index, or die */
static int checkintX (lua_State *L, int index) {
   int result = 0;
   if (lua_isuserdata(L, index) && luaL_checkudata(L, index, "integer")) {
      result = *(int*)lua_touserdata(L, index);
   } else if (lua_isnumber(L, index)) {
      double val = lua_tonumber(L, index);
      int x = (int) val;
      result = (int) lua_tonumber(L, index);
   } else {
      lua_pushstring(L, "Invalid operand. Expected 'integer' or 'number'");
      lua_error(L);
   }
   return result;
}

static int checkint(lua_State* L, int index) {
   int result = checkintX(L, index);
   return result;
}

static int create_integer(lua_State* L, int val) {
   int* ud = lua_newuserdata(L, sizeof(int));
   *ud = val;
   luaL_getmetatable(L, "integer");
   lua_setmetatable(L, -2);
   return 1;
}

static int int_new (lua_State* L) { return create_integer( L, checkint(L,1) ); }
static int int_add (lua_State* L) { return create_integer( L, checkint(L,1) + checkint(L,2) ); }
static int int_sub (lua_State* L) { return create_integer( L, checkint(L,1) - checkint(L,2) ); }
static int int_mul (lua_State* L) { return create_integer( L, checkint(L,1) * checkint(L,2) ); }
static int int_div (lua_State* L) { return create_integer( L, checkint(L,1) / checkint(L,2) ); }
static int int_mod (lua_State* L) { return create_integer( L, checkint(L,1) % checkint(L,2) ); }
static int int_pow (lua_State* L) { return create_integer( L, (int) pow( checkint(L,1), checkint(L,2) ) ); }
static int int_unm (lua_State* L) { return create_integer( L, -checkint(L,1) ); }
static int int_eq  (lua_State* L) { lua_pushboolean( L, checkint(L,1) == checkint(L,2) ); return 1; }
static int int_lt  (lua_State* L) { lua_pushboolean( L, checkint(L,1) <  checkint(L,2) ); return 1; }
static int int_le  (lua_State* L) { lua_pushboolean( L, checkint(L,1) <= checkint(L,2) ); return 1; }
static int int_tostring (lua_State* L) {
   lua_pushnumber(L, checkint(L,1));
   lua_tostring(L, -1);
   return 1;
}

int __declspec(dllexport) __cdecl luaopen_integer (lua_State* L) {
   static const struct luaL_reg integermt[] = {
      { "__add", int_add },
      { "__sub", int_sub },
      { "__mul", int_mul },
      { "__div", int_div },
      { "__mod", int_mod },
      { "__pow", int_pow },
      { "__unm", int_unm },
      { "__eq",  int_eq  },
      { "__lt",  int_lt  },
      { "__le",  int_le  },
      { "__tostring", int_tostring},
      NULL, NULL
   };
   luaL_newmetatable(L, "integer");
   luaL_openlib(L, NULL, integermt, 0);

   lua_register(L, "int", int_new);

   return 0;
}

Why do you need them? The best way to help you find a performant solution to your problem is to understand the problem. What specifically do you need integers for?

The main performance penalty from what i gather is (of course) the very numerous allocations.

Well, you're creating closures on every operation, and I don't understand why you have a Double class at all, given that Lua's number type is already a double. Couldn't you do something like this?

Integer = {}
local function val(o) return type(o) == 'number' and o or o[1] end
function Integer.__add(a,b) return Integer:create(val(a) + val(b)) end
function Integer.__sub(a,b) return Integer:create(val(a) - val(b)) end
function Integer.__div(a,b) return Integer:create(val(a) / val(b)) end
function Integer.__mul(a,b) return Integer:create(val(a) * val(b)) end
function Integer:__tostring() return tostring(self[1]) end
function Integer:create(value)
   return setmetatable({math.floor(value)}, Integer)
end


-- test
a = Integer:create(15.34)
b = Integer:create(775.34433)
print((a*10/2+b-3)/3*a+b) --> 5005

Anybody think it would be possible to do better with a custom c implementation and userdata?

Yes, a C implementation should be faster, because you wouldn't need to create a table for each Integer; your userdata could literally just be an int*. This would also eliminate the need for the floor call.

EDIT: I wrote a test C implementation and it's ~5 times faster than the Lua implementation presented in this post.

EDIT 2: Link to C code is dead, so here's some code I found buried in my hard disk which may or may not be the code that was at that link. I know that it compiles against Lua 5.1, but beyond that.. no clue if it's even a technically valid implementation. Been too long since I wrote any Lua C code.

#include <math.h>
#include "lua.h"
#include "lauxlib.h"

/* get value of Integer userdata or Lua number at index, or die */
static int checkintX (lua_State *L, int index) {
   int result = 0;
   if (lua_isuserdata(L, index) && luaL_checkudata(L, index, "integer")) {
      result = *(int*)lua_touserdata(L, index);
   } else if (lua_isnumber(L, index)) {
      double val = lua_tonumber(L, index);
      int x = (int) val;
      result = (int) lua_tonumber(L, index);
   } else {
      lua_pushstring(L, "Invalid operand. Expected 'integer' or 'number'");
      lua_error(L);
   }
   return result;
}

static int checkint(lua_State* L, int index) {
   int result = checkintX(L, index);
   return result;
}

static int create_integer(lua_State* L, int val) {
   int* ud = lua_newuserdata(L, sizeof(int));
   *ud = val;
   luaL_getmetatable(L, "integer");
   lua_setmetatable(L, -2);
   return 1;
}

static int int_new (lua_State* L) { return create_integer( L, checkint(L,1) ); }
static int int_add (lua_State* L) { return create_integer( L, checkint(L,1) + checkint(L,2) ); }
static int int_sub (lua_State* L) { return create_integer( L, checkint(L,1) - checkint(L,2) ); }
static int int_mul (lua_State* L) { return create_integer( L, checkint(L,1) * checkint(L,2) ); }
static int int_div (lua_State* L) { return create_integer( L, checkint(L,1) / checkint(L,2) ); }
static int int_mod (lua_State* L) { return create_integer( L, checkint(L,1) % checkint(L,2) ); }
static int int_pow (lua_State* L) { return create_integer( L, (int) pow( checkint(L,1), checkint(L,2) ) ); }
static int int_unm (lua_State* L) { return create_integer( L, -checkint(L,1) ); }
static int int_eq  (lua_State* L) { lua_pushboolean( L, checkint(L,1) == checkint(L,2) ); return 1; }
static int int_lt  (lua_State* L) { lua_pushboolean( L, checkint(L,1) <  checkint(L,2) ); return 1; }
static int int_le  (lua_State* L) { lua_pushboolean( L, checkint(L,1) <= checkint(L,2) ); return 1; }
static int int_tostring (lua_State* L) {
   lua_pushnumber(L, checkint(L,1));
   lua_tostring(L, -1);
   return 1;
}

int __declspec(dllexport) __cdecl luaopen_integer (lua_State* L) {
   static const struct luaL_reg integermt[] = {
      { "__add", int_add },
      { "__sub", int_sub },
      { "__mul", int_mul },
      { "__div", int_div },
      { "__mod", int_mod },
      { "__pow", int_pow },
      { "__unm", int_unm },
      { "__eq",  int_eq  },
      { "__lt",  int_lt  },
      { "__le",  int_le  },
      { "__tostring", int_tostring},
      NULL, NULL
   };
   luaL_newmetatable(L, "integer");
   luaL_openlib(L, NULL, integermt, 0);

   lua_register(L, "int", int_new);

   return 0;
}
烟酒忠诚 2024-10-15 08:12:53

Lua 5.3 中刚刚添加了整数(默认为 64 位)!

http://www.lua.org/versions.html#5.3

Integers (64-bit by default) were just added in Lua 5.3!

http://www.lua.org/versions.html#5.3

荭秂 2024-10-15 08:12:53

如果您想要处理整数,您可以随时在luaconf.h#define LUA_NUMBER int

If you want to only deal with integers you could always #define LUA_NUMBER int in luaconf.h.

夏末染殇 2024-10-15 08:12:53

您可以尝试 http://www. 列出的任意精度库之一。 tecgraf.puc-rio.br/~lhf/ftp/lua/ 。特别是, lbn 仅是整数并且速度很快,但是你需要 OpenSSL。对于简单的独立库,请参阅 lbc

You can try one of the arbitrary precision libraries listed at http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/ . In particular, lbn is integer-only and fast but you need OpenSSL. For a simple self-contained library, see lbc.

浮光之海 2024-10-15 08:12:53

您可以尝试 LNUM 补丁,它修改 Lua 核心以添加整数数字类型,例如 32 和 64位整数和双精度数。它还支持复数。

You could try the LNUM patch, it modifies the Lua core to add integer numeric types, like 32 and 64 bit integers along with doubles. It also supports complex numbers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文