Lua - 毫秒级当前时间

84

有没有一种常见的方法可以获取当前时间(包括毫秒)?

虽然有os.time()方法,但它只提供整秒。

12个回答

65

我使用LuaSocket以获得更高的精度。

require "socket"
print("Milliseconds: " .. socket.gettime()*1000)

这会增加一个依赖项,但对于个人使用(例如在基准测试脚本中)有效。


2
请注意,至少有一位用户声称在Windows上,该实现不具备足够的高分辨率:http://lua-users.org/wiki/HiResTimer - Phrogz
1
请注意,我的系统在更新后,这个程序就不再起作用了。我不得不将其更改为 socket = require "socket",否则 socket 将为空值。 - phemmer

61

如果您想进行基准测试,您可以使用文档中所示的os.clock:

local x = os.clock()
local s = 0
for i=1,100000 do s = s + i end
print(string.format("elapsed time: %.2f\n", os.clock() - x))

9
不是毫秒级别,而是精度为1/100秒。 - schaul
3
如果您调用一个使用线程的C函数,这种方法就不起作用了。它报告的是所有线程所需的时间总和,而不是实际的时间。 - Ciprian Tomoiagă
1
这个函数以及os.time函数高度依赖于系统。还要注意文档中说明它返回程序使用的CPU时间,在大多数系统上与实际花费的"地球时间"非常不同。 - dualed
3
由于我被限制使用标准Lua,并且必须保持可移植性和封装性(不能使用外部dll),这是我能找到的最佳解决方案。+1 - Richard
@ValerioSchiavoni,您能否提供一下您所参考文档的链接?正如文档所示。 - Shane Bishop
2
@ShaneBishop 请查看https://www.lua.org/pil/22.1.html页面底部。 - Valerio Schiavoni

38
在标准的C语言版lua中,不行。除非你愿意修改lua解释器自身,以便os.time使用你想要的分辨率。然而,如果你正在为其他人编写代码而不是像Web应用程序那样完全控制环境的东西,这可能是不可接受的。
另一个选择是编写自己的小型C语言动态链接库(DLL),通过扩展一个新函数来扩充lua,从而得到所需的值,并要求将该dll与你的代码一起分发给任何将要使用它的人。

6
取决于系统,DLL 或 .so 等。 :-) - PhiLho
os.clock() 返回的是什么小数? - Kousha
@Kousha,os.clock 中的小数是秒的分数;但是,os.clock 报告的是程序执行时间而不是真实世界时间。在 Windows 系统上,os.clock 还不一致地报告 real timecpu time - Nifim

20

获取当前时间的毫秒表示。

os.time()

os.time()
return sec // only

posix.clock_gettime(clk)

https://luaposix.github.io/luaposix/modules/posix.time.html#clock_gettime

require'posix'.clock_gettime(0)
return sec, nsec

linux/time.h // man clock_gettime

/*
 * The IDs of the various system clocks (for POSIX.1b interval timers):
 */
#define CLOCK_REALTIME                  0
#define CLOCK_MONOTONIC                 1
#define CLOCK_PROCESS_CPUTIME_ID        2
#define CLOCK_THREAD_CPUTIME_ID         3
#define CLOCK_MONOTONIC_RAW             4
#define CLOCK_REALTIME_COARSE           5
#define CLOCK_MONOTONIC_COARSE          6

socket.gettime()

http://w3.impa.br/~diego/software/luasocket/socket.html#gettime

require'socket'.gettime()
return sec.xxx

正如waqas所说


比较和测试

get_millisecond.lua

local posix=require'posix'
local socket=require'socket'

for i=1,3 do
    print( os.time() )
    print( posix.clock_gettime(0) )
    print( socket.gettime() )
    print''
    posix.nanosleep(0, 1) -- sec, nsec
end

输出

lua get_millisecond.lua
1490186718
1490186718      268570540
1490186718.2686

1490186718
1490186718      268662191
1490186718.2687

1490186718
1490186718      268782765
1490186718.2688

11

我为Windows上的Lua制作了一个合适的解决方案。基本上我做了Kevlar建议的事情,但使用了共享库而不是DLL,在cygwin中进行了测试。

我编写了一些与Lua兼容的C代码,使用gcc在cygwin中将其编译为共享库(.so文件),然后使用package.cpath和require" "在Lua中加载它。为方便起见编写了适配器脚本。以下是所有的源代码:

首先是C代码HighResTimer.c

////////////////////////////////////////////////////////////////
//HighResTimer.c by Cody Duncan
//
//compile with:  gcc -o Timer.so -shared HighResTimer.c -llua5.1
//compiled in cygwin after installing lua (cant remember if I 
//   installed via setup or if I downloaded and compiled lua, 
//   probably the former)
////////////////////////////////////////////////////////////////
#include <windows.h>

typedef unsigned __int64 u64;
double mNanoSecondsPerCount;

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"


int prevInit = 0;
int currInit = 0;
u64 prevTime = 0;
u64 currTime = 0;
u64 FrequencyCountPerSec;

LARGE_INTEGER frequencyTemp;
static int readHiResTimerFrequency(lua_State *L)
{
    QueryPerformanceFrequency(&frequencyTemp);
    FrequencyCountPerSec = frequencyTemp.QuadPart;
    lua_pushnumber(L, frequencyTemp.QuadPart);
    return 1;
}

LARGE_INTEGER timerTemp;
static int storeTime(lua_State *L)
{
    QueryPerformanceCounter(&timerTemp);

    if(!prevInit)
    {
        prevInit = 1;
        prevTime = timerTemp.QuadPart;
    }
    else if (!currInit)
    {
        currInit = 1;
        currTime = timerTemp.QuadPart;
    }
    else
    {
        prevTime = currTime;
        currTime = timerTemp.QuadPart;
    }

    lua_pushnumber(L, timerTemp.QuadPart);
    return 1;
}

static int getNanoElapsed(lua_State *L)
{
    double mNanoSecondsPerCount = 1000000000/(double)FrequencyCountPerSec;
    double elapsedNano = (currTime - prevTime)*mNanoSecondsPerCount;
    lua_pushnumber(L, elapsedNano);
    return 1;
}


int luaopen_HighResolutionTimer (lua_State *L) {

    static const luaL_reg mylib [] = 
    {
        {"readHiResTimerFrequency", readHiResTimerFrequency},
        {"storeTime", storeTime},
        {"getNanoElapsed", getNanoElapsed},
        {NULL, NULL}  /* sentinel */
    };

    luaL_register(L,"timer",mylib);

    return 1;
}

--

现在让我们把它加载到 Lua 脚本中,命名为 HighResTimer.lua。

注意:我已经将 HighResTimer.c 编译成共享库 Timer.so。

#!/bin/lua
------------------------------------
---HighResTimer.lua by Cody Duncan
---Wraps the High Resolution Timer Functions in
---   Timer.so
------------------------------------

package.cpath = "./Timer.so"     --assuming Timer.so is in the same directory
require "HighResolutionTimer"    --load up the module
timer.readHiResTimerFrequency(); --stores the tickFrequency


--call this before code that is being measured for execution time
function start()
    timer.storeTime();
end

--call this after code that is being measured for execution time
function stop()
    timer.storeTime();
end

--once the prior two functions have been called, call this to get the 
--time elapsed between them in nanoseconds
function getNanosElapsed()
    return timer.getNanoElapsed();
end

--

最后,使用计时器 TimerTest.lua。

#!/bin/lua
------------------------------------
---TimerTest.lua by Cody Duncan
---
---HighResTimer.lua and Timer.so must 
---   be in the same directory as 
---   this script.
------------------------------------

require './HighResTimer' 

start();
for i = 0, 3000000 do io.write("") end --do essentially nothing 3million times.
stop();

--divide nanoseconds by 1 million to get milliseconds
executionTime = getNanosElapsed()/1000000; 
io.write("execution time: ", executionTime, "ms\n");

注意:任何评论都是在将源代码粘贴到文章编辑器后编写的,因此从技术上讲,这是未经测试的,但希望这些评论不会对任何事情造成困扰。如果有问题,我一定会回来提供修复方案。


很有用!如果有人想将其移植到Mac或Linux,可以使用此处的高分辨率C代码:https://github.com/tylerneylon/oswrap/blob/master/oswrap_mac/now.c - Tyler
@Tyler:你会如何从lua中调用它? - SuperJedi224
@SuperJedi224 你需要创建一个C包装器,类似于 int getHighResTime(lua_State *L) { /* 将时间推入lua堆栈 */ return 1; },添加代码以在Lua中注册C函数,然后使用Lua C API将其编译为共享库。这是一个不错的关于该过程的PDF文档:http://cs.brynmawr.edu/Courses/cs380/fall2011/luar-topics2.pdf - Tyler
我也会避免清除现有的 package.cpath;相反,我会使用 package.cpath = "./Timer.so;" .. package.cpath 来将其前置。 - SlySven
在Windows上,您可以从LuaRT调用sys.clock()函数。 - TSam
太棒了!非常感谢。如果您能为像我这样的新手提供构建dll,那就是100/10! - Ismoh

7
如果您正在使用lua与nginx/openresty,您可以使用ngx.now()函数,该函数返回毫秒精度的浮点数。

从文档中:“返回一个浮点数,表示从nginx缓存时间戳开始到当前时间的经过时间(包括毫秒作为小数部分),单位为秒(与Lua的日期库不同,无需系统调用)。”这是错误的 https://github.com/openresty/lua-nginx-module#ngxnow - smartius
你真的很不礼貌。你不能断言我所写的是“错误”的。 我只是提出了一个可能的解决方案(我用于负载测试,并且它完美地运行),并且我链接了文档(所以对此感兴趣的人可以查看详细信息)。 - deepskyblue86
此外,如果您查看源代码,您所建议使用的ngx.req.start_time也使用了缓存时间(ngx_timeofday)。因此,您可能会得到与ngx.now相同或更早的结果。 - deepskyblue86
抱歉我之前可能有些失礼,我真心道歉。但如果我理解正确的话,ngx.now() 返回的是请求开始时的时间戳 + 当前脚本运行到实际调用 ngx.now() 的时间。 - smartius
不,这是一个纪元时间戳。ngx.now()和ngx.req.start_time()都在内部使用ngx_timeofday(),它是nginx缓存的时间(尽管经常更新)。 因此,两个函数返回相同的值可能会发生,或者最有可能是彼此接近但不同的值。 - deepskyblue86
最近遇到了同样的问题。解决方案似乎是使用ngx.update_time()前缀的ngx.now(),以强制更新缓存时间,如https://github.com/openresty/lua-nginx-module#ngxupdate_time所述。 - esboych

4
如果您正在使用OpenResty,则可以通过其ngx.now()函数提供内置的毫秒时间精度。虽然如果您想要更细粒度的毫秒精度,则可能需要先调用ngx.update_time()。或者,如果您想再进一步......
如果您正在使用启用了luajit的环境,例如OpenResty,则还可以使用ffi来访问基于C的时间函数,例如gettimeofday(),例如:(注意:pcall检查struct timeval的存在仅在重复运行它时才是必需的,例如通过OpenResty中的content_by_lua_file - 如果没有它,您会遇到错误,例如attempt to redefine 'timeval'
if pcall(ffi.typeof, "struct timeval") then
        -- check if already defined.
else
        -- undefined! let's define it!
        ffi.cdef[[
           typedef struct timeval {
                long tv_sec;
                long tv_usec;
           } timeval;

        int gettimeofday(struct timeval* t, void* tzp);
]]
end
local gettimeofday_struct = ffi.new("struct timeval")
local function gettimeofday()
        ffi.C.gettimeofday(gettimeofday_struct, nil)
        return tonumber(gettimeofday_struct.tv_sec) * 1000000 + tonumber(gettimeofday_struct.tv_usec)
end

然后,新的lua gettimeofday()函数可以从lua中调用,提供时钟时间到微秒级精度。实际上,使用clock_gettime()采取类似方法,可以获得纳秒精度。

最佳答案! - mvorisek

2

Kevlar是正确的。

一个替代自定义DLL的选择是Lua Alien


2
在OpenResty中有一个函数ngx.req.start_time
根据文档:
返回一个浮点数,表示当前请求创建时的时间戳(包括毫秒作为小数部分)。

不幸的是,在我的情况下,ngx.req.start_time()返回0。顺便说一下,os.clock()也是如此。我使用的Openresty版本是:"openresty/1.13.6.2"。 - esboych

2
如果你的系统有一个可执行的符合GNU标准的date实现,那么以下是获取毫秒级别的Epoch时间的一行代码:
local function gethammertime()
  return tonumber(assert(assert(io.popen'date +%s%3N'):read'a'))
end

请注意,assert 调用是必要的,以确保任何读取或打开 date 的失败都会相应地传播错误。还要注意,这依赖于垃圾回收(或 Lua 5.4 中的 finalizers)来关闭进程句柄:如果使用早于 Lua 5.4 的版本并且资源耗尽是一个问题,则可能希望将其扩展为三行,如 Klesun's Windows-based answer 并显式关闭句柄。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接