aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
authorLukas Larsson <[email protected]>2014-09-11 18:26:26 +0200
committerLukas Larsson <[email protected]>2016-02-02 10:45:21 +0100
commiteea5f896780e07f7ca76685061d01e7be5a7abaa (patch)
tree7d4a852b20f2075637457052844865e1c56b1e21 /lib/kernel
parentd76ee58c07f32dfc0652844ec2b513af2105ffa1 (diff)
downloadotp-eea5f896780e07f7ca76685061d01e7be5a7abaa.tar.gz
otp-eea5f896780e07f7ca76685061d01e7be5a7abaa.tar.bz2
otp-eea5f896780e07f7ca76685061d01e7be5a7abaa.zip
erts, kernel: Add os:perf_counter function
The perf_counter is a very very cheap and high resolution timer that can be used to timestamp system events. It does not have monoticity guarantees, but should on most OS's expose a monotonous time. A special instruction has been created for this counter to further speed up fetching it. OTP-12908
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/os.xml25
-rw-r--r--lib/kernel/src/os.erl16
-rw-r--r--lib/kernel/test/os_SUITE.erl46
3 files changed, 83 insertions, 4 deletions
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index 682d4a2eac..165160a909 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -37,6 +37,7 @@
use these functions can be of help in enabling a program to run on
most platforms.</p>
</description>
+
<funcs>
<func>
<name name="cmd" arity="1"/>
@@ -210,6 +211,30 @@ format_utc_timestamp() ->
</desc>
</func>
<func>
+ <name name="perf_counter" arity="0"/>
+ <fsummary>Returns a performance counter</fsummary>
+ <desc>
+ <p>Returns the current performance counter value in <c>perf_counter</c>
+ <seealso marker="erts:erlang#type_time_unit">time unit</seealso>.
+ This is a highly optimized call that might not be traceable.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name name="perf_counter" arity="1"/>
+ <fsummary>Returns a performance counter</fsummary>
+ <desc><p>Returns a performance counter that can be used as a very fast and
+ high resolution timestamp. This counter is read directly from the hardware or operating
+ system with the same guarantees. This means that two consecutive calls
+ to the function are not guaranteed to be monotonic, though it most likely will be.
+ The performance counter till be converted to the resolution passed as an argument.</p>
+ <pre>1> <input>T1 = os:perf_counter(1000),receive after 10000 -> ok end,T2 = os:perf_counter(1000).</input>
+176525861
+2> <input>T2 - T1.</input>
+10004</pre>
+ </desc>
+ </func>
+ <func>
<name name="type" arity="0"/>
<fsummary>Return the OS family and, in some cases, OS name of the current operating system</fsummary>
<desc>
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index 0022959c11..4947088635 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -27,7 +27,9 @@
%%% BIFs
--export([getenv/0, getenv/1, getenv/2, getpid/0, putenv/2, system_time/0, system_time/1,
+-export([getenv/0, getenv/1, getenv/2, getpid/0,
+ perf_counter/0, perf_counter/1,
+ putenv/2, system_time/0, system_time/1,
timestamp/0, unsetenv/1]).
-spec getenv() -> [string()].
@@ -60,6 +62,18 @@ getenv(VarName, DefaultValue) ->
getpid() ->
erlang:nif_error(undef).
+-spec perf_counter() -> Counter when
+ Counter :: integer().
+
+perf_counter() ->
+ erlang:nif_error(undef).
+
+-spec perf_counter(Unit) -> integer() when
+ Unit :: erlang:time_unit().
+
+perf_counter(Unit) ->
+ erlang:convert_time_unit(os:perf_counter(), perf_counter, Unit).
+
-spec putenv(VarName, Value) -> true when
VarName :: string(),
Value :: string().
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 83a95019e7..e6e8568394 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -20,10 +20,11 @@
-module(os_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2]).
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2]).
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
- large_output_command/1]).
+ large_output_command/1, perf_counter_api/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -32,7 +33,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
- large_output_command].
+ large_output_command, perf_counter_api].
groups() ->
[].
@@ -49,6 +50,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(_TC,Config) ->
+ Config.
+
+end_per_testcase(_,_Config) ->
+ ok.
space_in_cwd(doc) ->
"Test that executing a command in a current working directory "
@@ -278,6 +284,40 @@ large_output_command(Config) when is_list(Config) ->
AAA = lists:duplicate(7000, $a),
comp(AAA,os:cmd("echo " ++ AAA)).
+%% Test that the os:perf_counter api works as expected
+perf_counter_api(_Config) ->
+
+ true = is_integer(os:perf_counter()),
+ true = os:perf_counter() > 0,
+
+ T1 = os:perf_counter(),
+ timer:sleep(100),
+ T2 = os:perf_counter(),
+ TsDiff = erlang:convert_time_unit(T2 - T1, perf_counter, nano_seconds),
+ ct:pal("T1: ~p~n"
+ "T2: ~p~n"
+ "TsDiff: ~p~n",
+ [T1,T2,TsDiff]),
+
+ %% We allow a 15% diff
+ true = TsDiff < 115000000,
+ true = TsDiff > 85000000,
+
+ T1Ms = os:perf_counter(1000),
+ timer:sleep(100),
+ T2Ms = os:perf_counter(1000),
+ MsDiff = T2Ms - T1Ms,
+ ct:pal("T1Ms: ~p~n"
+ "T2Ms: ~p~n"
+ "MsDiff: ~p~n",
+ [T1Ms,T2Ms,MsDiff]),
+
+ %% We allow a 15% diff
+ true = MsDiff < 115,
+ true = MsDiff > 85.
+
+%% Util functions
+
comp(Expected, Got) ->
case strip_nl(Got) of
Expected ->