Ver código fonte

haxe.Timer.milliseconds (#12260)

* Add millisecond resolution timer function

* Just bin the fractional part since it's already ms

* It sort of works

* Only use python counter_ns function on 3.7 or above

* use wrapped Int64 on eval for now

* fence against hl_ver 1.16.0

* fix fallback overflow

---------

Co-authored-by: Simon Krajewski <[email protected]>
Aidan Lee 1 mês atrás
pai
commit
fbbea2c7d3

+ 2 - 0
libs/extc/extc.ml

@@ -51,6 +51,8 @@ external zlib_crc32 : bytes -> int -> int32 = "zlib_crc32"
 
 external time : unit -> float = "sys_time"
 
+external timestamp_ms : unit -> int64 = "sys_timestamp_ms"
+
 external getch : bool -> int = "sys_getch"
 
 external filetime : string -> float = "sys_filetime"

+ 25 - 2
libs/extc/extc_stubs.c

@@ -487,11 +487,14 @@ CAMLprim value get_real_path( value path ) {
 #define TimeSpecToSeconds(ts) (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0
 #endif
 
+#ifdef WIN32
+static LARGE_INTEGER freq;
+static int freq_init = -1;
+#endif
+
 CAMLprim value sys_time() {
 #ifdef _WIN32
 #define EPOCH_DIFF	(134774*24*60*60.0)
-	static LARGE_INTEGER freq;
-	static int freq_init = -1;
 	LARGE_INTEGER counter;
 	if( freq_init == -1 )
 		freq_init = QueryPerformanceFrequency(&freq);
@@ -533,6 +536,26 @@ CAMLprim value sys_time() {
 #endif
 }
 
+CAMLprim value sys_timestamp_ms() {
+#ifdef _WIN32
+	if (-1 == freq_init) {
+		freq_init = QueryPerformanceFrequency(&freq);
+	}
+
+	LARGE_INTEGER time;
+	QueryPerformanceCounter(&time);
+
+	return caml_copy_int64(time.QuadPart * 1000LL / freq.QuadPart);
+#else
+	struct timespec ts;
+	if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
+		caml_failwith("Failed to get time from the monotonic clock");
+	}
+
+	return caml_copy_int64(ts.tv_sec * 1000 + (ts.tv_nsec / 1000000));
+#endif
+}
+
 CAMLprim value sys_getch( value b ) {
 #	ifdef _WIN32
 	return Val_int( Bool_val(b)?getche():getch() );

+ 3 - 0
src/macro/eval/evalStdLib.ml

@@ -2709,6 +2709,8 @@ module StdSys = struct
 		)
 
 	let time = vfun0 (fun () -> vfloat (catch_unix_error Unix.gettimeofday()))
+
+	let timestamp_ms = vfun0 (fun () -> EvalIntegers.encode_haxe_i64_direct (* TODO: use vint64 once that works *) (Extc.timestamp_ms()))
 end
 
 module StdThread = struct
@@ -3727,6 +3729,7 @@ let init_standard_library builtins =
 		"stdout",StdSys.stdout;
 		"systemName",StdSys.systemName;
 		"time",StdSys.time;
+		"timestamp_ms",StdSys.timestamp_ms;
 	] [];
 	init_fields builtins (["eval";"vm"],"NativeThread") [
 		"delay",StdThread.delay;

+ 1 - 0
src/macro/eval/evalValue.ml

@@ -347,6 +347,7 @@ let vfield_closure v f = VFieldClosure(v,f)
 let vobject o = VObject o
 let vint i = VInt32 (Int32.of_int i)
 let vint32 i = VInt32 i
+let vint64 i = VInt64 i
 let vfloat f = VFloat f
 let venum_value e = VEnumValue e
 let vnative_string s = VNativeString s

+ 2 - 0
std/eval/_std/Sys.hx

@@ -75,6 +75,8 @@ class Sys {
 
 	extern static public function time():Float;
 
+	extern static function timestamp_ms():haxe.Int64;
+
 	extern static public function cpuTime():Float;
 
 	extern static public function programPath():String;

+ 36 - 1
std/haxe/Timer.hx

@@ -22,6 +22,7 @@
 
 package haxe;
 
+import haxe.Int64;
 #if (target.threaded && !cppia)
 import sys.thread.Thread;
 import sys.thread.EventLoop;
@@ -43,7 +44,7 @@ import sys.thread.EventLoop;
 
 	Notice for threaded targets:
 	`Timer` instances require threads they were created in to run with Haxe's event loops.
-	Main thread of a Haxe program always contains an event loop. For other cases use 
+	Main thread of a Haxe program always contains an event loop. For other cases use
 	`sys.thread.Thread.createWithEventLoop` and `sys.thread.Thread.runWithEventLoop` methods.
 **/
 class Timer {
@@ -195,4 +196,38 @@ class Timer {
 		return 0;
 		#end
 	}
+
+	/**
+	 * Returns a monotonically increasing timestamp with millisecond resolution.
+	 *
+	 * The precision and epoch of the timer is platform defined.
+	 */
+	public static inline function milliseconds():Int64 {
+		#if flash
+		return flash.Lib.getTimer();
+		#elseif js
+		#if nodejs
+		var hrtime = js.Syntax.code('process.hrtime()'); // [seconds, remaining nanoseconds]
+		return hrtime[0] * 1000 + (hrtime[1] / 1000000i64);
+		#else
+		return Std.int(@:privateAccess HxOverrides.now());
+		#end
+		#elseif cpp
+		return untyped __global__.__time_stamp_ms();
+		#elseif python
+		#if (python_version >= 3.7)
+		return python.lib.Time.perf_counter_ns() / 1000000i64;
+		#else
+		return Std.int(stamp() * 1000);
+		#end
+		#elseif (hl && hl_ver >= version("1.16.0"))
+		return hl.Api.timestampMs();
+		#elseif jvm
+		return java.lang.System.nanoTime() / 1000000i64;
+		#elseif eval
+		return @:privateAccess Sys.timestamp_ms();
+		#else
+		return Int64.mul(Int64.fromFloat(stamp()), 1000);
+		#end
+	}
 }

+ 5 - 2
std/hl/Api.hx

@@ -52,11 +52,14 @@ extern class Api {
 	#if (hl_ver >= version("1.13.0"))
 	@:hlNative("?std", "sys_has_debugger") static function hasDebugger() : Bool;
 	#end
-	#if (hl_ver >= version("1.15.0"))	
+	#if (hl_ver >= version("1.15.0"))
 	@:hlNative("?std", "register_guid_name") private static function _registerGUIDName( guid : hl.I64, bytes : hl.Bytes ) : Void;
 	static inline function registerGUIDName( guid : GUID, name : String ) {
 		return _registerGUIDName(guid,@:privateAccess name.bytes);
 	}
 	#end
-	
+	#if (hl_ver >= version("1.16.0"))
+	@:hlNative('std', 'sys_timestamp_ms')
+	static function timestampMs():haxe.Int64;
+	#end
 }

+ 1 - 0
std/python/lib/Time.hx

@@ -30,4 +30,5 @@ extern class Time {
 	static function clock():Float;
 	static function sleep(t:Float):Void;
 	static function mktime(s:StructTime):Float;
+	static function perf_counter_ns():Int;
 }