Created
May 15, 2013 09:03
-
-
Save sebmaynard/5582611 to your computer and use it in GitHub Desktop.
Generate a somewhat unique 64 bit number.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-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