Skip to content

Instantly share code, notes, and snippets.

@sebmaynard
Created May 15, 2013 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebmaynard/5582611 to your computer and use it in GitHub Desktop.
Save sebmaynard/5582611 to your computer and use it in GitHub Desktop.
Generate a somewhat unique 64 bit number.
-module(camisc_unique).
-export([semi_unique/0, get_timestamp/0, test/0]).
%% ===================================================================
%% API
%% ===================================================================
%% Generate a somewhat unique 64 bit number.
%%
%% Everytime this is called, it creates a sorted list of nodes
%% connected (including this one) and gets the "node index" in this
%% list; also creates a timestamp of which it uses 52 bits, and bors
%% the 2 together. 52 bits of timestamp is enough for 100 years from
%% now, leaving 12 bits for the node index - enough for 4096
%% nodes. Hopefully sufficient!
%%
%% Even though the nodeindex will change over time as nodes are added
%% and removed, if 2 given nodes call semi_unique at the exact same
%% microsecond their relative nodeindexes will still be different
%% (provided nodes() isn't halfway through changing on both nodes and
%% somehow generates the same nodeindex on both - not sure quite how
%% to cope with that though)
%%
%% Relies on the fact that erlang timestamps are unique on subsequent calls
semi_unique() ->
TS = get_timestamp(),
NodeIndex = get_current_nodeindex(),
%% the TS fits (if we assume this won't be running past 2112) in
%% 52 bits leaving 12 bits for the NodeIndex; this is enough for
%% 4096 nodes. I'm hoping we don't need more nodes than that....
(NodeIndex bsl 52) bor TS.
%% ===================================================================
%% Implementation
%% ===================================================================
%% get a timestamp number from a call to now()
get_timestamp() ->
get_timestamp(now()).
get_timestamp({Mega, Sec, Micro}) ->
Timestamp = Mega * 1000000 * 1000000 + Sec * 1000000 + Micro,
Timestamp.
from_timestamp(Timestamp) ->
{_TimestampMega, _TimestampSec, _TimestampMicro} = { (Timestamp div (1000000*1000000)),
(Timestamp div 1000000) rem (1000000),
Timestamp rem 1000000 }.
%% Get the index of an item in a list
get_index_in_list(Item, List) ->
get_index_in_list(Item, List, 0).
get_index_in_list(Item, [Item | _List], Count) ->
Count;
get_index_in_list(Item, [_ | Rest], Count) ->
get_index_in_list(Item, Rest, Count+1);
get_index_in_list(_Item, [], _Count) ->
not_found.
%% get the current node index - from a sorted list of connected nodes
%% and this one
get_current_nodeindex() ->
Node = node(),
Nodes = lists:sort([Node | nodes()]),
Index = get_index_in_list(Node, Nodes),
Index.
%% ===================================================================
%% Test functions
%% ===================================================================
%% borrowed from http://www.trapexit.org/Measuring_Function_Execution_Time
test_avg(M, F, A, N) when N > 0 ->
io:format("Testing ~p:~p(~p) ~p times~n", [M, F, string:join(A, ", "), N]),
L = test_loop(M, F, A, N, []),
Length = length(L),
Min = lists:min(L),
Max = lists:max(L),
Med = lists:nth(round((Length / 2)), lists:sort(L)),
Avg = round(lists:foldl(fun(X, Sum) ->
X + Sum end, 0, L) / Length),
io:format("Range: ~b - ~b mics~n"
"Median: ~b mics~n"
"Average: ~b mics~n",
[Min, Max, Med, Avg]),
ok.
test_loop(_M, _F, _A, 0, List) ->
List;
test_loop(M, F, A, N, List) ->
{T, _Result} = timer:tc(M, F, A),
test_loop(M, F, A, N - 1, [T|List]).
test_to_from_timestamp() ->
io:format("Testing timestamp/now functions~n"),
Orig = now(),
io:format("Orig: \t\t~p~n", [Orig]),
io:format("Date: \t\t~p~n", [calendar:now_to_universal_time(Orig)]),
TS = get_timestamp(Orig),
io:format("TS: \t\t~p~n", [TS]),
Converted = from_timestamp(TS),
io:format("Converted: \t~p~n", [Converted]),
io:format("Date: \t\t~p~n", [calendar:now_to_universal_time(Converted)]),
Converted = Orig,
io:format("~n~n"),
ok.
test() ->
test_to_from_timestamp(),
test_avg(unique, semi_unique, [], 100000).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment