├── 10k.txt ├── README ├── bench.hrl ├── binary-join.escript ├── binary-match.escript ├── binary-split.escript ├── comma-parse.escript ├── date-to-etag ├── drop-last.escript ├── element-lookup.escript ├── file-type.escript ├── function-dispatch.escript ├── index_lookup.escript ├── list-concat ├── membership-check.escript ├── name-lookup.escript ├── path-split.escript ├── split-large-text.escript ├── string-flatten.escript ├── string-join.escript └── strip-binary.escript /10k.txt: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is an assortment of Erlang rellated benchmarks 2 | 3 | Assuming you have Erlang installed, you can just execute the applicable escript 4 | to view the results. 5 | 6 | Of course, review the source before you execute anything! 7 | -------------------------------------------------------------------------------- /bench.hrl: -------------------------------------------------------------------------------- 1 | bench(Name, Fun, Trials) -> 2 | print_result(Name, repeat_tc(Fun, Trials)). 3 | 4 | repeat_tc(Fun, Trials) -> 5 | {Time, _} = timer:tc(fun() -> repeat(Trials, Fun) end), 6 | {Time, Trials}. 7 | 8 | repeat(0, _Fun) -> ok; 9 | repeat(N, Fun) -> Fun(), repeat(N - 1, Fun). 10 | 11 | print_result(Name, {Time, Trials}) -> 12 | io:format("~s: ~.3f us (~.2f per second)~n", 13 | [Name, Time / Trials, Trials / (Time / 1000000)]). 14 | -------------------------------------------------------------------------------- /binary-join.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the most efficient way combine a list of binaries into a single 4 | %%% binary? 5 | %%% 6 | %%% The obvious approach is iolist_to_binary/1. But there's also binary 7 | %%% append. 8 | %%% 9 | %%% Accoding to this: 10 | %%% 11 | %%% http://www.erlang.org/doc/efficiency_guide/binaryhandling.html 12 | %%% 13 | %%% The append operation is efficient - binaries are allocated with 14 | %%% size(B) * 2 when they need more space. 15 | %%% 16 | %%% Typical results on my laptop on R16B03 are: 17 | %%% 18 | %%% iolist_to_binary: 96 19 | %%% binary_append: 610 20 | %%% 21 | %%% I'm a little surprised, though I have no basis for surpsie. Clearly 22 | %%% iolist_to_binary/1, in the case where just binaries are joined, is the 23 | %%% way to go. Unless I'm missing something. 24 | %%% 25 | -mode(compile). 26 | 27 | -include("bench.hrl"). 28 | 29 | -define(PART, <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>). 30 | -define(PART_COUNT, 1000). 31 | 32 | -define(TRIALS, 10000). 33 | 34 | main(_) -> 35 | test_iolist_to_binary(), 36 | test_binary_append(). 37 | 38 | test_iolist_to_binary() -> 39 | Parts = lists:duplicate(?PART_COUNT, ?PART), 40 | Target = iolist_to_binary(Parts), 41 | bench( 42 | "iolist_to_binary", 43 | fun() -> iolist_to_binary_join(Parts, Target) end, 44 | ?TRIALS). 45 | 46 | iolist_to_binary_join(Parts, Target) -> 47 | Target = iolist_to_binary(Parts). 48 | 49 | test_binary_append() -> 50 | Parts = lists:duplicate(?PART_COUNT, ?PART), 51 | Target = iolist_to_binary(Parts), 52 | bench( 53 | "binary_append", 54 | fun() -> binary_append(Parts, Target) end, 55 | ?TRIALS). 56 | 57 | binary_append(Parts, Target) -> 58 | Target = binary_append_acc(Parts, <<>>). 59 | 60 | binary_append_acc([Part|Rest], Acc) -> 61 | binary_append_acc(Rest, <>); 62 | binary_append_acc([], Acc) -> Acc. 63 | -------------------------------------------------------------------------------- /binary-match.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the most efficient way to find the offset of a binary pattern? 4 | %%% 5 | %%% binary:match/3 does that, but it uses a "pattern" - I don't know how 6 | %%% efficient this is, when compared to a scan of the binary using the 7 | %%% split_binary or binary_part functions, or pattern matching. 8 | %%% 9 | %%% I think there are four ways to do this: 10 | %%% 11 | %%% - binary:match/2 (using compiled and uncompiled patterns) 12 | %%% - scan using split_binary/2 13 | %%% - scan using binary_part/2 14 | %%% - scan using binary pattern matching 15 | %%% 16 | %%% Typical results on my laptop under R16B: 17 | %%% 18 | %%% binary_match_compiled: 56 19 | %%% binary_match_uncompiled: 50 20 | %%% scan_split: 3971 21 | %%% scan_part: 1638 22 | %%% scan_pattern: 2975 23 | %%% 24 | %%% binary:match/2 is obviously optimizes for this. There's no material 25 | %%% difference between the compiled pattern and uncompiled. 26 | %%% 27 | -mode(compile). 28 | 29 | -include("bench.hrl"). 30 | 31 | -define(NEEDLE, <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>). 32 | -define(NEEDLE_CP, binary:compile_pattern(?NEEDLE)). 33 | 34 | -define(TRIALS, 100000). 35 | 36 | main(_) -> 37 | test_binary_match_compiled(), 38 | test_binary_match_uncompiled(), 39 | test_scan_split(), 40 | test_scan_part(), 41 | test_scan_pattern(). 42 | 43 | haystack() -> 44 | NeedleRev = lists:reverse(binary_to_list(?NEEDLE)), 45 | Parts = 46 | [lists:duplicate(20, NeedleRev), 47 | ?NEEDLE, 48 | lists:duplicate(20, NeedleRev)], 49 | NeedlePos = size(?NEEDLE) * 20, 50 | {NeedlePos, iolist_to_binary(Parts)}. 51 | 52 | test_binary_match_compiled() -> 53 | Haystack = haystack(), 54 | bench( 55 | "binary_match_compiled", 56 | fun() -> binary_match(?NEEDLE_CP, Haystack) end, 57 | ?TRIALS). 58 | 59 | test_binary_match_uncompiled() -> 60 | Haystack = haystack(), 61 | bench( 62 | "binary_match_uncompiled", 63 | fun() -> binary_match(?NEEDLE, Haystack) end, 64 | ?TRIALS). 65 | 66 | binary_match(Pattern, {Pos, Haystack}) -> 67 | {Pos, _} = binary:match(Haystack, Pattern). 68 | 69 | test_scan_split() -> 70 | Haystack = haystack(), 71 | bench( 72 | "scan_split", 73 | fun() -> scan_split(?NEEDLE, Haystack) end, 74 | ?TRIALS). 75 | 76 | scan_split(Needle, {Pos, Haystack}) -> 77 | NeedleSize = size(Needle), 78 | Start = 0, 79 | Stop = size(Haystack) - NeedleSize, 80 | Pos = scan_split(Needle, NeedleSize, Haystack, Start, Stop). 81 | 82 | scan_split(Needle, NeedleSize, Haystack, Pos, Stop) when Pos =< Stop -> 83 | case split_binary(Haystack, Pos) of 84 | {_, <>} -> Pos; 85 | _ -> scan_split(Needle, NeedleSize, Haystack, Pos + 1, Stop) 86 | end; 87 | scan_split(_, _, _, _, _) -> not_found. 88 | 89 | test_scan_part() -> 90 | Haystack = haystack(), 91 | bench( 92 | "scan_part", 93 | fun() -> scan_part(?NEEDLE, Haystack) end, 94 | ?TRIALS). 95 | 96 | scan_part(Needle, {Pos, Haystack}) -> 97 | NeedleSize = size(Needle), 98 | Start = 0, 99 | Stop = size(Haystack) - NeedleSize, 100 | Pos = scan_part(Needle, NeedleSize, Haystack, Start, Stop). 101 | 102 | scan_part(Needle, NeedleSize, Haystack, Pos, Stop) when Pos =< Stop -> 103 | case binary_part(Haystack, Pos, NeedleSize) of 104 | Needle -> Pos; 105 | _ -> scan_part(Needle, NeedleSize, Haystack, Pos + 1, Stop) 106 | end; 107 | scan_part(_, _, _, _, _) -> not_found. 108 | 109 | test_scan_pattern() -> 110 | Haystack = haystack(), 111 | bench( 112 | "scan_pattern", 113 | fun() -> scan_pattern(?NEEDLE, Haystack) end, 114 | ?TRIALS). 115 | 116 | scan_pattern(Needle, {Pos, Haystack}) -> 117 | NeedleSize = size(Needle), 118 | Start = 0, 119 | Stop = size(Haystack) - NeedleSize, 120 | Pos = scan_pattern(Needle, NeedleSize, Haystack, Start, Stop). 121 | 122 | scan_pattern(Needle, NeedleSize, Haystack, Pos, Stop) when Pos =< Stop -> 123 | case Haystack of 124 | <<_:Pos/binary, Needle:NeedleSize/binary, _/binary>> -> Pos; 125 | _ -> scan_pattern(Needle, NeedleSize, Haystack, Pos + 1, Stop) 126 | end; 127 | scan_pattern(_, _, _, _, _) -> not_found. 128 | -------------------------------------------------------------------------------- /binary-split.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the most efficient way to split a binary into two parts using a 4 | %%% pattern? 5 | %%% 6 | %%% This is the sister benchmark to binary-match. 7 | %%% 8 | %%% There are three approaches I can think of: 9 | %%% 10 | %%% - Use binary:match/2 to find the pattern position and split_binary 11 | %%% to return the parts 12 | %%% - Use binary:split/2 13 | %%% - Use pattern matching 14 | %%% 15 | %%% Typical results on my laptop under R16B: 16 | %%% 17 | %%% binary_match: 67 18 | %%% binary_split: 79 19 | %%% binary_pattern: 3617 20 | %%% 21 | %%% Based on binary-match.escript results, nothing surprising here. 22 | %%% binary:split/2 is the one to use (though strangely, with more moving parts, 23 | %%% the binary:match/2 method is ever so slightly faster). 24 | %%% 25 | -mode(compile). 26 | 27 | -include("bench.hrl"). 28 | 29 | -define(NEEDLE, <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16>>). 30 | 31 | -define(TRIALS, 100000). 32 | 33 | main(_) -> 34 | test_binary_match(), 35 | test_binary_split(), 36 | test_binary_pattern(). 37 | 38 | haystack() -> 39 | NeedleRev = lists:reverse(binary_to_list(?NEEDLE)), 40 | Parts = 41 | [lists:duplicate(20, NeedleRev), 42 | ?NEEDLE, 43 | lists:duplicate(20, NeedleRev)], 44 | iolist_to_binary(Parts). 45 | 46 | test_binary_match() -> 47 | Haystack = haystack(), 48 | Target = binary:split(Haystack, ?NEEDLE), 49 | bench( 50 | "binary_match", 51 | fun() -> binary_match(?NEEDLE, Haystack, Target) end, 52 | ?TRIALS). 53 | 54 | binary_match(Needle, Haystack, Target) -> 55 | {Pos, Len} = binary:match(Haystack, Needle), 56 | {P1, Rest} = split_binary(Haystack, Pos), 57 | {_, P2} = split_binary(Rest, Len), 58 | Target = [P1, P2]. 59 | 60 | test_binary_split() -> 61 | Haystack = haystack(), 62 | Target = binary:split(Haystack, ?NEEDLE), 63 | bench( 64 | "binary_split", 65 | fun() -> binary_split(?NEEDLE, Haystack, Target) end, 66 | ?TRIALS). 67 | 68 | binary_split(Needle, Haystack, Target) -> 69 | Target = binary:split(Haystack, Needle). 70 | 71 | 72 | test_binary_pattern() -> 73 | Haystack = haystack(), 74 | Target = binary:split(Haystack, ?NEEDLE), 75 | bench( 76 | "binary_pattern", 77 | fun() -> binary_pattern(?NEEDLE, Haystack, Target) end, 78 | ?TRIALS). 79 | 80 | binary_pattern(Needle, Haystack, Target) -> 81 | NeedleSize = size(Needle), 82 | Start = 0, 83 | Stop = size(Haystack) - NeedleSize, 84 | Target = binary_pattern(Needle, NeedleSize, Haystack, Start, Stop). 85 | 86 | binary_pattern(Needle, NeedleSize, Haystack, Pos, Stop) when Pos =< Stop -> 87 | case Haystack of 88 | <> -> [P1, P2]; 89 | _ -> binary_pattern(Needle, NeedleSize, Haystack, Pos + 1, Stop) 90 | end; 91 | binary_pattern(_, _, _, _, _) -> not_found. 92 | -------------------------------------------------------------------------------- /comma-parse.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% This test is driven by erlydtl's inability to support yesno filter with 4 | %%% empty values. E.g. "var|yesno:'if-true,'" will fail. It shouldn't. 5 | %%% 6 | %%% The yesno filter uses string:token/2 to parse the string. I was curious 7 | %%% if switching to re:split/3 would cause a significant performance hit. 8 | %%% 9 | %%% Typical results on my laptop under R16B: 10 | %%% 11 | %%% string_tokens: 291 12 | %%% uncompiled_re_split: 2515 13 | %%% compiled_re_split: 2051 14 | %%% binary_split: 311 15 | %%% binary_split_with_convert: 457 16 | %%% 17 | -mode(compile). 18 | 19 | -include("bench.hrl"). 20 | 21 | -define(TRIALS, 100000). 22 | -define(STRINGS, 23 | ["yes,no,default", 24 | "yes,", 25 | "yes,,", 26 | "01234567890123456789,01234567890123456789,01234567890123456789"]). 27 | -define(COMMA, ","). 28 | -define(COMMA_REGEX, 29 | {re_pattern,0,0, 30 | <<69,82,67,80,57,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,44,0,0,0,48,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,0,5,27,44, 32 | 84,0,5,0>>}). 33 | 34 | main(_) -> 35 | test_string_tokens(), 36 | test_uncompiled_re_split(), 37 | test_compiled_re_split(), 38 | test_bin_split(), 39 | test_convert_bin_split(). 40 | 41 | test_string_tokens() -> 42 | bench( 43 | "string_tokens", 44 | fun() -> string_tokenize(?STRINGS, ?COMMA) end, 45 | ?TRIALS). 46 | 47 | string_tokenize([S|Rest], Delimiter) -> 48 | string:tokens(S, Delimiter), 49 | string_tokenize(Rest, Delimiter); 50 | string_tokenize([], _Delim) -> ok. 51 | 52 | test_uncompiled_re_split() -> 53 | bench( 54 | "uncompiled_re_split", 55 | fun() -> re_split(?STRINGS, ?COMMA) end, 56 | ?TRIALS). 57 | 58 | re_split([S|Rest], Pattern) -> 59 | re:split(S, Pattern, [{return, list}]), 60 | re_split(Rest, Pattern); 61 | re_split([], _Pattern) -> ok. 62 | 63 | test_compiled_re_split() -> 64 | bench( 65 | "compiled_re_split", 66 | fun() -> re_split(?STRINGS, ?COMMA_REGEX) end, 67 | ?TRIALS). 68 | 69 | test_bin_split() -> 70 | BinStrings = [list_to_binary(S) || S <- ?STRINGS], 71 | BinComma = list_to_binary(?COMMA), 72 | bench( 73 | "binary_split", 74 | fun() -> bin_split(BinStrings, BinComma) end, 75 | ?TRIALS). 76 | 77 | bin_split([S|Rest], Sep) -> 78 | binary:split(S, Sep, [global]), 79 | bin_split(Rest, Sep); 80 | bin_split([], _Sep) -> ok. 81 | 82 | test_convert_bin_split() -> 83 | bench( 84 | "binary_split_with_convert", 85 | fun() -> convert_bin_split(?STRINGS, ?COMMA) end, 86 | ?TRIALS). 87 | 88 | convert_bin_split([S|Rest], Sep) -> 89 | SBin = list_to_binary(S), 90 | SepBin = list_to_binary(Sep), 91 | _ = [binary_to_list(Part) || Part <- binary:split(SBin, SepBin, [global])], 92 | convert_bin_split(Rest, Sep); 93 | convert_bin_split([], _Sep) -> ok. 94 | -------------------------------------------------------------------------------- /date-to-etag: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the fastest way to generate an etag [1] from a date? 4 | %%% 5 | %%% The date is initially represented as a datetime() as per the 6 | %%% the calendar module. [2] 7 | %%% 8 | %%% [1] https://tools.ietf.org/html/rfc7232#section-2.3 9 | %%% 10 | %%% [2] http://erlang.org/doc/man/calendar.html 11 | %%% 12 | %%% Options: 13 | %%% 14 | %%% - base64 encode a term_to_binary of the date 15 | %%% - base64 encode a term_to_binary of the date as int 16 | %%% - base64 a hand encoded date as binary 17 | %%% - String of a hash of the date 18 | %%% - String of the date epoch 19 | %%% - String representation of the date 20 | %%% 21 | %%% Sample results: 22 | %%% 23 | %%% date_term_to_binary: 1.636 us (611153.18 per second) 24 | %%% int_term_to_binary: 0.881 us (1135648.69 per second) 25 | %%% date_to_binary: 0.746 us (1339992.20 per second) 26 | %%% hash: 0.156 us (6424505.63 per second) 27 | %%% epoch_string: 0.299 us (3344112.52 per second) 28 | %%% date_string: 0.215 us (4645717.58 per second) 29 | %%% 30 | %%% Not surprisingly, the obvious losers are the ones that use base64 31 | %%% encoding. 32 | %%% 33 | -mode(compile). 34 | 35 | -include("bench.hrl"). 36 | 37 | -define(date, {{2016,12,21},{20,30,37}}). 38 | 39 | -define(TRIALS, 1000000). 40 | 41 | main(_) -> 42 | test_date_term_to_binary(), 43 | test_int_term_to_binary(), 44 | test_date_to_binary(), 45 | test_hash(), 46 | test_epoch_string(), 47 | test_date_string(). 48 | 49 | test_date_term_to_binary() -> 50 | bench( 51 | "date_term_to_binary", 52 | fun() -> date_term_to_binary(?date) end, 53 | ?TRIALS). 54 | 55 | date_term_to_binary(D) -> 56 | Encoded = term_to_binary(D), 57 | <<"g2gCaANiAAAH4GEMYRVoA2EUYR5hJQ==">> = base64:encode(Encoded). 58 | 59 | test_int_term_to_binary() -> 60 | bench( 61 | "int_term_to_binary", 62 | fun() -> int_term_to_binary(?date) end, 63 | ?TRIALS). 64 | 65 | int_term_to_binary(D) -> 66 | Timestamp = calendar:datetime_to_gregorian_seconds(D) - 62167219200, 67 | Encoded = term_to_binary(Timestamp), 68 | <<"g2JYWuZt">> = base64:encode(Encoded). 69 | 70 | test_date_to_binary() -> 71 | bench( 72 | "date_to_binary", 73 | fun() -> date_to_binary(?date) end, 74 | ?TRIALS). 75 | 76 | date_to_binary({{Y, M, D}, {H, N, S}}) -> 77 | Encoded = <>, 78 | <<"B+AMFRQeJQ==">> = base64:encode(Encoded). 79 | 80 | test_hash() -> 81 | bench( 82 | "hash", 83 | fun() -> hash(?date) end, 84 | ?TRIALS). 85 | 86 | hash(D) -> 87 | "2520201254" = integer_to_list(erlang:phash2(D, 4294967296)). 88 | 89 | test_epoch_string() -> 90 | bench( 91 | "epoch_string", 92 | fun() -> epoch_string(?date) end, 93 | ?TRIALS). 94 | 95 | epoch_string(D) -> 96 | Timestamp = calendar:datetime_to_gregorian_seconds(D) - 62167219200, 97 | "1482352237" = integer_to_list(Timestamp). 98 | 99 | test_date_string() -> 100 | bench( 101 | "date_string", 102 | fun() -> date_string(?date) end, 103 | ?TRIALS). 104 | 105 | date_string({{Y, M, D}, {H, N, S}}) -> 106 | ["2016","12","21","20", "30","37"] 107 | = [integer_to_list(Y), 108 | integer_to_list(M), 109 | integer_to_list(D), 110 | integer_to_list(H), 111 | integer_to_list(N), 112 | integer_to_list(S)]. 113 | -------------------------------------------------------------------------------- /drop-last.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% This test is driven by a proposal to implement a drop_last function in 4 | %%% Erlang's lists module. 5 | %%% 6 | %%% http://erlang.org/pipermail/erlang-patches/2013-April/003851.html 7 | %%% 8 | %%% Representative of my results (laptop): 9 | %%% 10 | %%% reverse_reverse:small_list: 166 11 | %%% reverse_reverse:big_list: 3017 12 | %%% recurse:small_list: 210 13 | %%% recurse:small_list: 3115 14 | %%% sublist:small_list: 316 15 | %%% sublist:big_list: 9558 16 | %%% 17 | -mode(compile). 18 | 19 | -include("bench.hrl"). 20 | 21 | -define(SMALL_LIST, lists:seq(1, 100)). 22 | -define(SMALL_LIST_TRIALS, 100000). 23 | 24 | -define(BIG_LIST, lists:seq(1, 1000000)). 25 | -define(BIG_LIST_TRIALS, 100). 26 | 27 | main(_) -> 28 | test_reverse_reverse(), 29 | test_recurse(), 30 | test_sublist(). 31 | 32 | test_reverse_reverse() -> 33 | bench( 34 | "reverse_reverse:small_list", 35 | fun() -> reverse_reverse(?SMALL_LIST) end, 36 | ?SMALL_LIST_TRIALS), 37 | bench( 38 | "reverse_reverse:big_list", 39 | fun() -> reverse_reverse(?BIG_LIST) end, 40 | ?BIG_LIST_TRIALS). 41 | 42 | reverse_reverse(L) -> 43 | lists:reverse(tl(lists:reverse(L))). 44 | 45 | test_recurse() -> 46 | bench( 47 | "recurse:small_list", 48 | fun() -> recurse(?SMALL_LIST) end, 49 | ?SMALL_LIST_TRIALS), 50 | bench( 51 | "recurse:big_list", 52 | fun() -> recurse(?BIG_LIST) end, 53 | ?BIG_LIST_TRIALS). 54 | 55 | recurse([_]) -> []; 56 | recurse([H|T]) -> [H|recurse(T)]. 57 | 58 | test_sublist() -> 59 | bench( 60 | "sublist:small_list", 61 | fun() -> sublist(?SMALL_LIST) end, 62 | ?SMALL_LIST_TRIALS), 63 | bench( 64 | "sublist:big_list", 65 | fun() -> sublist(?BIG_LIST) end, 66 | ?BIG_LIST_TRIALS). 67 | 68 | sublist(L) -> 69 | lists:sublist(L, 1, length(L) - 1). 70 | -------------------------------------------------------------------------------- /element-lookup.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% element-lookup.escript 3 | %%% 4 | %%% This is the most myopic of tests! I was curious which of these two forms 5 | %%% is "faster": 6 | %%% 7 | %%% {_, Value} = {ok, Value} 8 | %%% element(2, {ok, Value}) 9 | %%% 10 | %%% Pretty stupid, I know. But I'm curious! 11 | %%% 12 | %%% These results are representative on my laptop (R16B): 13 | %%% 14 | %%% match: 344 15 | %%% element_fun: 183 16 | %%% 17 | %%% A direct tuple element lookup appears to be ~2x faster than an 18 | %%% efficient-looking pattern match bind. 19 | %%% 20 | -mode(compile). 21 | 22 | -include("bench.hrl"). 23 | 24 | -define(VALUE, "Hello"). 25 | -define(TUPLE, {ok, ?VALUE}). 26 | 27 | -define(TRIALS, 10000000). 28 | 29 | main(_) -> 30 | test_match(), 31 | test_element_fun(). 32 | 33 | test_match() -> 34 | bench("match", fun() -> {_, ?VALUE} = ?TUPLE end, ?TRIALS). 35 | 36 | test_element_fun() -> 37 | bench("element_fun", fun() -> ?VALUE = element(2, ?TUPLE) end, ?TRIALS). 38 | -------------------------------------------------------------------------------- /file-type.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | % file-type.escript 3 | % 4 | % What's the fastest way to test whether a file is a file or a directory? 5 | % 6 | % There's filelib:is_file/1 and filelib:is_dir/1. There's also 7 | % file:read_file_info/1. Which is built for speed? 8 | % 9 | % filelib_is: 2773 10 | % read_file_info: 2746 11 | % 12 | % A dead heat. He he - should have checked! The filelib_is functions use 13 | % read_file_info. Doh! Good to know our tests are sane at least. 14 | % 15 | -mode(compile). 16 | 17 | -include("bench.hrl"). 18 | -include_lib("kernel/include/file.hrl"). 19 | 20 | -define(FILES, 100000). 21 | 22 | main(_) -> 23 | {Tmp, Files} = create_tmp_files(), 24 | try 25 | test_filelib_is(Files), 26 | test_read_file_info(Files) 27 | after 28 | cleanup(Tmp) 29 | end. 30 | 31 | %------------------------------------------------------------------- 32 | % Setup 33 | %------------------------------------------------------------------- 34 | 35 | create_tmp_files() -> 36 | io:format("Creating tmp files... "), 37 | TmpDir = create_tmp_dir(), 38 | Files = create_tmp_files(TmpDir, lists:seq(1, ?FILES)), 39 | io:format("ok~n"), 40 | {TmpDir, Files}. 41 | 42 | create_tmp_dir() -> 43 | Rand = erlang:phash2(os:timestamp()), 44 | Tmp = ["/tmp/file-type-bench-", integer_to_list(Rand)], 45 | ok = file:make_dir(Tmp), 46 | Tmp. 47 | 48 | create_tmp_files(Tmp, Seq) -> 49 | [create_file_or_dir(Tmp, N) || N <- Seq]. 50 | 51 | create_file_or_dir(Tmp, N) -> 52 | case N rem 2 of 53 | 0 -> {dir, create_dir(Tmp, N)}; 54 | 1 -> {file, create_file(Tmp, N)} 55 | end. 56 | 57 | create_dir(Parent, N) -> 58 | Dir = [Parent, "/", "d", integer_to_list(N)], 59 | ok = file:make_dir(Dir), 60 | Dir. 61 | 62 | create_file(Parent, N) -> 63 | File = [Parent, "/", "f", integer_to_list(N)], 64 | ok = file:write_file(File, ""), 65 | File. 66 | 67 | cleanup(["/tmp/file-type-bench-"|_]=Tmp) -> 68 | io:format("Deleting tmp files... "), 69 | "" = os:cmd("rm -rf " ++ Tmp), 70 | io:format("ok~n"). 71 | 72 | %------------------------------------------------------------------- 73 | % Shared 74 | %------------------------------------------------------------------- 75 | 76 | verify_file_types([{Type, File}|Rest], Fun) -> 77 | Type = Fun(File), 78 | verify_file_types(Rest, Fun); 79 | verify_file_types([], _Fun) -> 80 | ok. 81 | 82 | %------------------------------------------------------------------- 83 | % filelib_is 84 | %------------------------------------------------------------------- 85 | 86 | test_filelib_is(Files) -> 87 | bench( 88 | "filelib_is", 89 | fun() -> verify_file_types(Files, fun filelib_file_type/1) end, 90 | 1). 91 | 92 | filelib_file_type(File) -> 93 | case filelib:is_dir(File) of 94 | true -> dir; 95 | false -> file 96 | end. 97 | 98 | %------------------------------------------------------------------- 99 | % read_file_info 100 | %------------------------------------------------------------------- 101 | 102 | test_read_file_info(Files) -> 103 | bench( 104 | "read_file_info", 105 | fun() -> verify_file_types(Files, fun read_info_file_type/1) end, 106 | 1). 107 | 108 | read_info_file_type(File) -> 109 | case file:read_file_info(File) of 110 | {ok, #file_info{type=directory}} -> dir; 111 | {ok, #file_info{type=regular}} -> file 112 | end. 113 | -------------------------------------------------------------------------------- /function-dispatch.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the most efficient way to dispatch a message to a function? 4 | %%% 5 | %%% The use case driving this is to associate a function handler with a 6 | %%% HTTP request, which contains a method and a path. 7 | %%% 8 | %%% The two ways that are obvious: 9 | %%% 10 | %%% - Erlang function clauses 11 | %%% - Map lookup 12 | %%% 13 | %%% Typical results on my laptop under R16B: 14 | %%% 15 | %%% function_clauses: 186 16 | %%% dict: 610 17 | %%% 18 | %%% So with a relatively small set of messages, Erlang function pattern 19 | %%% matching, even though it's a linear scan, seems like a better option than 20 | %%% dict lookups. 21 | %%% 22 | -mode(compile). 23 | 24 | -include("bench.hrl"). 25 | 26 | -define( 27 | MSGS, 28 | ["p0575t9+KEQpHY9f", 29 | "EI82ApdVWn0tjD/e", 30 | "XACxSMLs3O5+W35y", 31 | "I55Lntpr+tErZNqy", 32 | "MRY50c6bjnckJ/Yn", 33 | "VmwLpUin2lpWFnJN", 34 | "jGWFTqKPUbul1s4L", 35 | "xytkylrSOKGviFrT", 36 | "/TV8RbJAnBouSfqQ", 37 | "WKq038qaueZM9sBf", 38 | "WBv/Q81iwamxmP1B", 39 | "GF7Vn041ikzBb0DI", 40 | "Yas3EljeM8Yjz7sZ", 41 | "S4Z3upBXzwdblF5L", 42 | "iY2jsmh0H5kudQKi", 43 | "uNROG44uhqSj1J2U", 44 | "GLe6KrdC9Ta5mwCu", 45 | "s0Ieiaq6ilT5cf1r", 46 | "p8WPVCrEdRZ1aqB7", 47 | "9Td9Enk02pMNxNSa", 48 | "K2Vo+O4yZjo2xfg3", 49 | "TxnHuLYLjqSyKlY5", 50 | "Ykk8LaqiDnEHNmDL", 51 | "pBUcpulFtxI7Cp3z", 52 | "7btg5o1A70yS5Am2", 53 | "yP5nrLykp+Dl4aDK", 54 | "Uplv6IOx011Tg3c8", 55 | "q9jmXTyKvTCxXD+T", 56 | "ROSO5FZihF2gpATE", 57 | "lCVenvgW+zN4qlRh"]). 58 | 59 | -define(TRIALS, 100000). 60 | 61 | main(_) -> 62 | test_function_clauses(), 63 | test_dict(). 64 | 65 | test_function_clauses() -> 66 | bench( 67 | "function_clauses", 68 | fun() -> function_clauses(?MSGS) end, 69 | ?TRIALS). 70 | 71 | function_clauses([Msg|Rest]) -> 72 | Msg = dispatch(Msg), 73 | function_clauses(Rest); 74 | function_clauses([]) -> ok. 75 | 76 | dispatch("p0575t9+KEQpHY9f"=Msg) -> Msg; 77 | dispatch("EI82ApdVWn0tjD/e"=Msg) -> Msg; 78 | dispatch("XACxSMLs3O5+W35y"=Msg) -> Msg; 79 | dispatch("I55Lntpr+tErZNqy"=Msg) -> Msg; 80 | dispatch("MRY50c6bjnckJ/Yn"=Msg) -> Msg; 81 | dispatch("VmwLpUin2lpWFnJN"=Msg) -> Msg; 82 | dispatch("jGWFTqKPUbul1s4L"=Msg) -> Msg; 83 | dispatch("xytkylrSOKGviFrT"=Msg) -> Msg; 84 | dispatch("/TV8RbJAnBouSfqQ"=Msg) -> Msg; 85 | dispatch("WKq038qaueZM9sBf"=Msg) -> Msg; 86 | dispatch("WBv/Q81iwamxmP1B"=Msg) -> Msg; 87 | dispatch("GF7Vn041ikzBb0DI"=Msg) -> Msg; 88 | dispatch("Yas3EljeM8Yjz7sZ"=Msg) -> Msg; 89 | dispatch("S4Z3upBXzwdblF5L"=Msg) -> Msg; 90 | dispatch("iY2jsmh0H5kudQKi"=Msg) -> Msg; 91 | dispatch("uNROG44uhqSj1J2U"=Msg) -> Msg; 92 | dispatch("GLe6KrdC9Ta5mwCu"=Msg) -> Msg; 93 | dispatch("s0Ieiaq6ilT5cf1r"=Msg) -> Msg; 94 | dispatch("p8WPVCrEdRZ1aqB7"=Msg) -> Msg; 95 | dispatch("9Td9Enk02pMNxNSa"=Msg) -> Msg; 96 | dispatch("K2Vo+O4yZjo2xfg3"=Msg) -> Msg; 97 | dispatch("TxnHuLYLjqSyKlY5"=Msg) -> Msg; 98 | dispatch("Ykk8LaqiDnEHNmDL"=Msg) -> Msg; 99 | dispatch("pBUcpulFtxI7Cp3z"=Msg) -> Msg; 100 | dispatch("7btg5o1A70yS5Am2"=Msg) -> Msg; 101 | dispatch("yP5nrLykp+Dl4aDK"=Msg) -> Msg; 102 | dispatch("Uplv6IOx011Tg3c8"=Msg) -> Msg; 103 | dispatch("q9jmXTyKvTCxXD+T"=Msg) -> Msg; 104 | dispatch("ROSO5FZihF2gpATE"=Msg) -> Msg; 105 | dispatch("lCVenvgW+zN4qlRh"=Msg) -> Msg. 106 | 107 | test_dict() -> 108 | D = dict:from_list([{Msg, fun() -> Msg end} || Msg <- ?MSGS]), 109 | bench( 110 | "dict", 111 | fun() -> dict(?MSGS, D) end, 112 | ?TRIALS). 113 | 114 | dict([Msg|Rest], Dict) -> 115 | Msg = (dict:fetch(Msg, Dict))(), 116 | dict(Rest, Dict); 117 | dict([], _Dict) -> ok. 118 | -------------------------------------------------------------------------------- /index_lookup.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the most efficient way to get the Nth item from a list of items? 4 | %%% 5 | %%% Approaches: 6 | %%% 7 | %%% - Erlang list 8 | %%% - Erlang "array" 9 | %%% - Tuple 10 | %%% 11 | %%% Results on my laptop under 17: 12 | %%% 13 | %%% list: 3493 14 | %%% array: 316 15 | %%% tuple: 67 16 | %%% 17 | -mode(compile). 18 | 19 | -include("bench.hrl"). 20 | 21 | -define(TRIALS, 10000). 22 | -define(NUM_ITEMS, 200). 23 | -define(ITEM, 'X'). 24 | 25 | main(_) -> 26 | Items = lists:duplicate(?NUM_ITEMS, ?ITEM), 27 | test_list(Items), 28 | test_array(Items), 29 | test_tuple(Items). 30 | 31 | test_list(Items) -> 32 | Max = length(Items), 33 | Get = fun lists:nth/2, 34 | bench("list", fun() -> lookup(Items, 1, Max, Get) end, ?TRIALS). 35 | 36 | test_array(Items) -> 37 | Array = array:from_list(Items), 38 | Max = length(Items), 39 | Get = fun array:get/2, 40 | bench("array", fun() -> lookup(Array, 0, Max - 1, Get) end, ?TRIALS). 41 | 42 | test_tuple(Items) -> 43 | Tuple = list_to_tuple(Items), 44 | Max = length(Items), 45 | Get = fun element/2, 46 | bench("tuple", fun() -> lookup(Tuple, 1, Max, Get) end, ?TRIALS). 47 | 48 | lookup(Items, N, Max, Get) when N =< Max -> 49 | ?ITEM = Get(N, Items), 50 | lookup(Items, N + 1, Max, Get); 51 | lookup(_, _, _, _) -> ok. 52 | -------------------------------------------------------------------------------- /list-concat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% What's the most efficient way to concat a bunch of lists? 4 | %%% 5 | %%% A few options come to mind: 6 | %%% 7 | %%% - lists:concat/1 8 | %%% - L1 ++ L2 ++ ... 9 | %%% - Cons plus reverse 10 | %%% 11 | %%% Typical results on my laptop on Erlang/OTP 18 are: 12 | %%% 13 | %%% concat: 7.670 us (130379.80 per second) 14 | %%% plusplus: 237.529 us (4210.01 per second) 15 | %%% cons: 12.969 us (77105.76 per second) 16 | %%% 17 | %%% And the winner is... the function that was designed to this very 18 | %%% thing! 19 | %%% 20 | -mode(compile). 21 | 22 | -include("bench.hrl"). 23 | 24 | -define(L, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]). 25 | -define(CONCATS, 100). 26 | 27 | -define(TRIALS, 10000). 28 | 29 | main(_) -> 30 | test_concat(), 31 | test_plusplus(), 32 | test_cons(). 33 | 34 | test_concat() -> 35 | Lol = lists:duplicate(?CONCATS, ?L), 36 | Target = lists:concat(Lol), 37 | bench( 38 | "concat", 39 | fun() -> concat(Lol, Target) end, 40 | ?TRIALS). 41 | 42 | concat(Lol, Target) -> 43 | Target = lists:concat(Lol). 44 | 45 | test_plusplus() -> 46 | Lol = lists:duplicate(?CONCATS, ?L), 47 | Target = lists:concat(Lol), 48 | bench( 49 | "plusplus", 50 | fun() -> plusplus(Lol, Target) end, 51 | ?TRIALS). 52 | 53 | plusplus(Lol, Target) -> 54 | plusplus_acc(Lol, Target, []). 55 | 56 | plusplus_acc([L|Rest], Target, Acc) -> 57 | plusplus_acc(Rest, Target, Acc ++ L); 58 | plusplus_acc([], Target, Acc) -> 59 | Target = Acc. 60 | 61 | test_cons() -> 62 | Lol = lists:duplicate(?CONCATS, ?L), 63 | Target = lists:concat(Lol), 64 | bench( 65 | "cons", 66 | fun() -> cons(Lol, Target) end, 67 | ?TRIALS). 68 | 69 | cons(Lol, Target) -> 70 | cons_acc(Lol, Target, []). 71 | 72 | cons_acc([L|Rest], Target, Acc) -> 73 | cons_acc(Rest, Target, cons_acc(L, Acc)); 74 | cons_acc([], Target, Acc) -> 75 | Target = lists:reverse(Acc). 76 | 77 | cons_acc([X|Rest], Acc) -> 78 | cons_acc(Rest, [X|Acc]); 79 | cons_acc([], Acc) -> 80 | Acc. 81 | -------------------------------------------------------------------------------- /membership-check.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% membership-check.escript 3 | %%% 4 | %%% I had a case where I needed to test whether a value was a member of a small 5 | %%% set (~20) of strings. The obvious choices in Erlang for this are: set, 6 | %%% dict, and list. 7 | %%% 8 | %%% The time measures repeated lookup of each member (to test for a match) and 9 | %%% a lookup of each member reverse (to test for a non match). 10 | %%% 11 | %%% These are representative of the results on my laptop (R16B) 12 | %%% 13 | %%% set: 685 14 | %%% dict: 718 15 | %%% list: 396 16 | %%% 17 | %%% Interestingly, the simplest approach (use a list!) appears to be as fast 18 | %%% (faster) than the other two. 19 | %%% 20 | -mode(compile). 21 | 22 | -include("bench.hrl"). 23 | 24 | -define( 25 | NAMES, 26 | ["memory_heap_used", 27 | "memory_ps_old_gen_committed", 28 | "proc_stat", 29 | "proc_rss", 30 | "threads_count", 31 | "classes_loaded", 32 | "memory_ps_perm_gen_peakCommitted", 33 | "pidstat_cpu", 34 | "memory_heap_max", 35 | "proc_vsz", 36 | "classes_loaded", 37 | "memory_ps_perm_gen_peakUsed", 38 | "request_requestCount", 39 | "request_processingTime", 40 | "request_errorCount", 41 | "pidstat_system", 42 | "memory_ps_survivor_space_committed"]). 43 | 44 | -define(TRIALS, 100000). 45 | 46 | main(_) -> 47 | Names = ?NAMES, 48 | NotNames = reverse_each(?NAMES), 49 | test_set(Names, NotNames), 50 | test_dict(Names, NotNames), 51 | test_list(Names, NotNames). 52 | 53 | reverse_each(Strings) -> 54 | [lists:reverse(S) || S <- Strings]. 55 | 56 | test_set(Names, NotNames) -> 57 | Set = sets:from_list(Names), 58 | bench( 59 | "set", 60 | fun() -> 61 | check_each_in_set(Names, true, Set), 62 | check_each_in_set(NotNames, false, Set) 63 | end, 64 | ?TRIALS). 65 | 66 | check_each_in_set([], _Expected, _Set) -> ok; 67 | check_each_in_set([Name|Rest], Expected, Set) -> 68 | Expected = sets:is_element(Name, Set), 69 | check_each_in_set(Rest, Expected, Set). 70 | 71 | test_dict(Names, NotNames) -> 72 | Dict = dict:from_list([{Name, 0} || Name <- ?NAMES]), 73 | bench( 74 | "dict", 75 | fun() -> 76 | check_each_in_dict(Names, true, Dict), 77 | check_each_in_dict(NotNames, false, Dict) 78 | end, 79 | ?TRIALS). 80 | 81 | check_each_in_dict([], _Expected, _Dict) -> ok; 82 | check_each_in_dict([Name|Rest], Expected, Dict) -> 83 | Expected = dict:is_key(Name, Dict), 84 | check_each_in_dict(Rest, Expected, Dict). 85 | 86 | test_list(Names, NotNames) -> 87 | bench( 88 | "list", 89 | fun() -> 90 | check_each_in_list(Names, true, Names), 91 | check_each_in_list(NotNames, false, Names) 92 | end, 93 | ?TRIALS). 94 | 95 | check_each_in_list([], _Expected, _List) -> ok; 96 | check_each_in_list([Name|Rest], Expected, List) -> 97 | Expected = lists:member(Name, List), 98 | check_each_in_list(Rest, Expected, List). 99 | -------------------------------------------------------------------------------- /name-lookup.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% name-lookup.escript 3 | %%% 4 | %%% For smallish lists of items that need to be retrieved using a simple name, 5 | %%% what is the fastest structure? 6 | %%% 7 | %%% proplists provide a canonical structure and are almost ubiquitous. But 8 | %%% proplists are just convention that are compatible with the proplists 9 | %%% module. Is there a substantial performance benefit to using one approach 10 | %%% over the other? 11 | %%% 12 | %%% And what about dict and trees? 13 | %%% 14 | %%% The time measures repeated lookup of each member (to test for a match) and 15 | %%% a lookup of each member reverse (to test for a non match). 16 | %%% 17 | %%% These are representative of the results on my laptop (R16B) 18 | %%% 19 | %%% proplists: 680 20 | %%% lists: 227 21 | %%% dict: 361 22 | %%% gb_tree: 215 23 | %%% 24 | %%% As dict and gb_tree require an initial transformation of the property list, 25 | %%% they might not make sense in any general case (these benchmarks only 26 | %%% measure lookup time and don't include the time to create the structure). 27 | %%% 28 | %%% The clear winner here is lists (this is already well known). It's curious 29 | %%% why proplists doesn't use lists:keyfind. 30 | %%% 31 | -mode(compile). 32 | 33 | -include("bench.hrl"). 34 | 35 | -define(VALUE, 9999999). 36 | 37 | -define( 38 | VALUES, 39 | [{"memory_heap_used", ?VALUE}, 40 | {"memory_ps_old_gen_committed", ?VALUE}, 41 | {"proc_stat", ?VALUE}, 42 | {"proc_rss", ?VALUE}, 43 | {"threads_count", ?VALUE}, 44 | {"classes_loaded", ?VALUE}, 45 | {"memory_ps_perm_gen_peakCommitted", ?VALUE}, 46 | {"pidstat_cpu", ?VALUE}, 47 | {"memory_heap_max", ?VALUE}, 48 | {"proc_vsz", ?VALUE}, 49 | {"classes_loaded", ?VALUE}, 50 | {"memory_ps_perm_gen_peakUsed", ?VALUE}, 51 | {"request_requestCount", ?VALUE}, 52 | {"request_processingTime", ?VALUE}, 53 | {"request_errorCount", ?VALUE}, 54 | {"pidstat_system", ?VALUE}, 55 | {"memory_ps_survivor_space_committed", ?VALUE}]). 56 | 57 | -define(TRIALS, 100000). 58 | 59 | main(_) -> 60 | Names = [Name || {Name, _} <- ?VALUES], 61 | test_proplists(Names, ?VALUES, ?VALUE), 62 | test_lists(Names, ?VALUES, ?VALUE), 63 | test_dict(Names, ?VALUES, ?VALUE), 64 | test_tree(Names, ?VALUES, ?VALUE). 65 | 66 | test_proplists(Names, Values, Expected) -> 67 | bench( 68 | "proplists", 69 | fun() -> lookup_names_in_proplist(Names, Values, Expected) end, 70 | ?TRIALS). 71 | 72 | lookup_names_in_proplist([Name|Rest], Proplist, Expected) -> 73 | Expected = proplists:get_value(Name, Proplist), 74 | lookup_names_in_proplist(Rest, Proplist, Expected); 75 | lookup_names_in_proplist([], _Proplist, _Expected) -> ok. 76 | 77 | test_lists(Names, Values, Expected) -> 78 | bench( 79 | "lists", 80 | fun() -> lookup_name_in_list(Names, Values, Expected) end, 81 | ?TRIALS). 82 | 83 | lookup_name_in_list([Name|Rest], Values, Expected) -> 84 | {_, Expected} = lists:keyfind(Name, 1, Values), 85 | lookup_name_in_list(Rest, Values, Expected); 86 | lookup_name_in_list([], _Values, _Expected) -> ok. 87 | 88 | test_dict(Names, Values, Expected) -> 89 | Dict = dict:from_list(Values), 90 | bench( 91 | "dict", 92 | fun() -> lookup_name_in_dict(Names, Dict, Expected) end, 93 | ?TRIALS). 94 | 95 | lookup_name_in_dict([Name|Rest], Dict, Expected) -> 96 | {ok, Expected} = dict:find(Name, Dict), 97 | lookup_name_in_dict(Rest, Dict, Expected); 98 | lookup_name_in_dict([], _Dict, _Expected) -> ok. 99 | 100 | test_tree(Names, Values, Expected) -> 101 | Tree = gb_trees:from_orddict(lists:sort(Values)), 102 | bench( 103 | "gb_tree", 104 | fun() -> lookup_name_in_tree(Names, Tree, Expected) end, 105 | ?TRIALS). 106 | 107 | lookup_name_in_tree([Name|Rest], Tree, Expected) -> 108 | {value, Expected} = gb_trees:lookup(Name, Tree), 109 | lookup_name_in_tree(Rest, Tree, Expected); 110 | lookup_name_in_tree([], _Tree, _Expected) -> ok. 111 | -------------------------------------------------------------------------------- /path-split.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% path-split.escript 3 | %%% 4 | %%% Given a path like "foo/bar/baz", what's the fastest way to split it into 5 | %%% its components using the path delimiter? 6 | %%% 7 | %%% Here are some obvious approaches: 8 | %%% 9 | %%% - re:split/3 (both on strings and binaries) 10 | %%% - binary:split/2 11 | %%% - string:tokens/2 12 | %%% - decons/cons recursive function 13 | %%% 14 | %%% These are representative of the results on my laptop (Erlang 18) 15 | %%% 16 | %%% re_string: 6.563 us (152363.77 per second) 17 | %%% re_binary: 5.923 us (168821.39 per second) 18 | %%% binary1: 1.147 us (872037.25 per second) 19 | %%% binary2: 1.319 us (757862.83 per second) 20 | %%% string: 0.444 us (2252404.44 per second) 21 | %%% decons: 0.528 us (1892398.24 per second) 22 | %%% 23 | %%% Regular expressions are, as expected, slower than the other 24 | %%% methods. Working with lists (string and decons) is quite fast. Of 25 | %%% course real word applications memory may be an issue. 26 | %%% 27 | -mode(compile). 28 | 29 | -include("bench.hrl"). 30 | 31 | -define(PATH, "aaaa/bbbb/cccc/dddd/eeee/ffff"). 32 | -define(DELIMITER, "/"). 33 | -define(PARTS, ["aaaa", "bbbb", "cccc", "dddd", "eeee", "ffff"]). 34 | 35 | -define(TRIALS, 100000). 36 | 37 | main(_) -> 38 | test_re_string(?PATH, ?DELIMITER, ?PARTS), 39 | test_re_binary(?PATH, ?DELIMITER, ?PARTS), 40 | test_binary1(?PATH, ?DELIMITER, ?PARTS), 41 | test_binary2(?PATH, ?DELIMITER, ?PARTS), 42 | test_string(?PATH, ?DELIMITER, ?PARTS), 43 | test_decons(?PATH, ?DELIMITER, ?PARTS). 44 | 45 | test_re_string(Path, Delimiter, Parts) -> 46 | bench( 47 | "re_string", 48 | fun() -> parse_re_string(Path, Delimiter, Parts) end, 49 | ?TRIALS). 50 | 51 | parse_re_string(Path, Delimiter, Parts) -> 52 | Parts = re:split(Path, Delimiter, [{return, list}]). 53 | 54 | test_re_binary(Path, Delimiter, Parts) -> 55 | PathBin = list_to_binary(Path), 56 | DelimiterBin = list_to_binary(Delimiter), 57 | PartsBin = [list_to_binary(Part) || Part <- Parts], 58 | bench( 59 | "re_binary", 60 | fun() -> parse_re_binary(PathBin, DelimiterBin, PartsBin) end, 61 | ?TRIALS). 62 | 63 | parse_re_binary(Path, Delimiter, Parts) -> 64 | Parts = re:split(Path, Delimiter). 65 | 66 | %% This version converts the path to a binary outside the benchmark 67 | %% test - so assumes that the path is already formatted as a binary. 68 | %% Refer to test_binary2 for a test that coverts the path to a binary 69 | %% inside the benchmark. 70 | %% 71 | test_binary1(Path, Delimiter, Parts) -> 72 | PathBin = list_to_binary(Path), 73 | DelimiterBin = list_to_binary(Delimiter), 74 | PartsBin = [list_to_binary(Part) || Part <- Parts], 75 | bench( 76 | "binary1", 77 | fun() -> binary_split_on_bin(PathBin, DelimiterBin, PartsBin) end, 78 | ?TRIALS). 79 | 80 | binary_split_on_bin(Path, Delimiter, Parts) -> 81 | Parts = binary:split(Path, Delimiter, [global]). 82 | 83 | %% This version convers the path to a binary (from a list using 84 | %% iolist_to_binary) as a part of the benchmark. 85 | %% 86 | test_binary2(Path, Delimiter, Parts) -> 87 | DelimiterBin = list_to_binary(Delimiter), 88 | PartsBin = [list_to_binary(Part) || Part <- Parts], 89 | bench( 90 | "binary2", 91 | fun() -> binary_split_on_iolist(Path, DelimiterBin, PartsBin) end, 92 | ?TRIALS). 93 | 94 | binary_split_on_iolist(Path, Delimiter, Parts) -> 95 | Parts = binary:split(iolist_to_binary(Path), Delimiter, [global]). 96 | 97 | test_string(Path, Delimiter, Parts) -> 98 | bench( 99 | "string", 100 | fun() -> parse_string(Path, Delimiter, Parts) end, 101 | ?TRIALS). 102 | 103 | parse_string(Path, Delimiter, Parts) -> 104 | Parts = string:tokens(Path, Delimiter). 105 | 106 | test_decons(Path, Delimiter, Parts) -> 107 | bench( 108 | "decons", 109 | fun() -> parse_decons(Path, Delimiter, Parts) end, 110 | ?TRIALS). 111 | 112 | parse_decons(Path, [Delimiter], Parts) -> 113 | Parts = parse_path(Path, Delimiter, "", []). 114 | 115 | parse_path([Delimiter|Rest], Delimiter, Cur, Acc) -> 116 | parse_path(Rest, Delimiter, "", [lists:reverse(Cur)|Acc]); 117 | parse_path([Char|Rest], Delimiter, Cur, Acc) -> 118 | parse_path(Rest, Delimiter, [Char|Cur], Acc); 119 | parse_path([], _Delimiter, Last, Acc) -> 120 | lists:reverse([lists:reverse(Last)|Acc]). 121 | -------------------------------------------------------------------------------- /split-large-text.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% path-split.escript 3 | %%% 4 | %%% If you're parsing a large block of text (e.g. a few K or more) 5 | %%% what's the more efficient method? 6 | %%% 7 | %%% This test is similar to path-split but deals with larger content. 8 | %%% 9 | %%% The test is to split an IO list consisting of 10k chunks separated 10 | %%% by a delimiter. 11 | %%% 12 | %%% These are representative of the results on my laptop (Erlang 18) 13 | %%% 14 | %%% re_string: 349.630 us (2860.17 per second) 15 | %%% re_binary: 60.838 us (16437.10 per second) 16 | %%% re_binary_compiled: 59.579 us (16784.44 per second) 17 | %%% binary_split: 157.960 us (6330.72 per second) 18 | %%% 19 | %%% In the case of these larger files, the regular expression out 20 | %%% performs binary split provided a binary is returned rather than a 21 | %%% list. 22 | %%% 23 | %%% It's interesting that compiling the pattern provides a negligible 24 | %%% improvement. 25 | %%% 26 | -mode(compile). 27 | 28 | -include("bench.hrl"). 29 | 30 | -define(CHUNK_FILE, "10k.txt"). 31 | -define(DELIMITER, <<"\n\n">>). 32 | -define(CHUNKS, 5). 33 | 34 | -define(TRIALS, 1000). 35 | 36 | main(_) -> 37 | {ok, Bin} = file:read_file(?CHUNK_FILE), 38 | Str = lists:duplicate(?CHUNKS, [Bin, ?DELIMITER]), 39 | Expected = ?CHUNKS + 1, 40 | test_re_string(Str, ?DELIMITER, Expected), 41 | test_re_binary(Str, ?DELIMITER, Expected), 42 | test_re_binary_compiled(Str, ?DELIMITER, Expected), 43 | test_binary_split(Str, ?DELIMITER, Expected). 44 | 45 | test_re_string(Str, Delim, Expected) -> 46 | bench( 47 | "re_string", 48 | fun() -> split_re_string(Str, Delim, Expected) end, 49 | ?TRIALS). 50 | 51 | split_re_string(Str, Delim, Expected) -> 52 | Parts = re:split(Str, Delim, [{return, list}]), 53 | Expected = length(Parts). 54 | 55 | test_re_binary(Str, Delim, Expected) -> 56 | bench( 57 | "re_binary", 58 | fun() -> split_re_binary(Str, Delim, Expected) end, 59 | ?TRIALS). 60 | 61 | split_re_binary(Str, Delim, Expected) -> 62 | Parts = re:split(Str, Delim, [{return, binary}]), 63 | Expected = length(Parts). 64 | 65 | test_re_binary_compiled(Str, Delim, Expected) -> 66 | {ok, Compiled} = re:compile(Delim), 67 | bench( 68 | "re_binary_compiled", 69 | fun() -> split_re_binary_compiled(Str, Compiled, Expected) end, 70 | ?TRIALS). 71 | 72 | split_re_binary_compiled(Str, Compiled, Expected) -> 73 | Parts = re:split(Str, Compiled, [{return, binary}]), 74 | Expected = length(Parts). 75 | 76 | test_binary_split(Str, Delim, Expected) -> 77 | bench( 78 | "binary_split", 79 | fun() -> split_binary(Str, Delim, Expected) end, 80 | ?TRIALS). 81 | 82 | split_binary(Str, Delim, Expected) -> 83 | Parts = binary:split(iolist_to_binary(Str), Delim, [global]), 84 | Expected = length(Parts). 85 | -------------------------------------------------------------------------------- /string-flatten.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% Flattening iolists to form "strings" in Erlang is generally a 4 | %%% waste time and space. Just pass the iolist along and most 5 | %%% everything will happily work with it. 6 | %%% 7 | %%% But what about APIs that overload an argument to be either a 8 | %%% "string" or "list of strings"? It's a bad practice, but sometimes 9 | %%% that's the hand you're dealt. 10 | %%% 11 | %%% What's the most efficient way to flatten an iolist into a so called 12 | %%% string? 13 | %%% 14 | %%% There's lists:flatten/1 - but this works only with lists won't 15 | %%% convert binaries along the way. Why would it? Still, for deeply 16 | %%% nested lists of ints, it will get us our precious string-looking 17 | %%% result. 18 | %%% 19 | %%% Another approach is to convert the iolist to binary with the 20 | %%% venerable iolist_to_binary/1, and convert that to a list. 21 | %%% 22 | %%% Here are the results on my laptop running 18. 23 | %%% 24 | %%% flatten: 780 25 | %%% binary_to_list: 478 26 | %%% 27 | %%% Here it looks like the seemingly more expensive two-step operation 28 | %%% of first converting the iolist to a binary and then converting the 29 | %%% binary to a list is slightly faster. 30 | %%% 31 | -mode(compile). 32 | 33 | -include("bench.hrl"). 34 | 35 | -define(TRIALS, 1000000). 36 | -define(TARGET, "sr0hZUwaVPJETArtq//HQx0YbX4ma3lcuCxzBH4UkGY2yNXz"). 37 | -define(IOLIST, [["sr0hZUwaVPJ"], ["ETArtq//HQx0Y", "bX4ma3lcuC"], 38 | "xzBH4UkGY2yNXz"]). 39 | 40 | main(_) -> 41 | test_flatten(), 42 | test_binary_to_list(). 43 | 44 | test_flatten() -> 45 | bench( 46 | "flatten", 47 | fun() -> flatten(?TARGET, ?IOLIST) end, 48 | ?TRIALS). 49 | 50 | flatten(Target, Data) -> 51 | Target = lists:flatten(Data). 52 | 53 | test_binary_to_list() -> 54 | bench( 55 | "binary_to_list", 56 | fun() -> binary_to_list(?TARGET, ?IOLIST) end, 57 | ?TRIALS). 58 | 59 | binary_to_list(Target, Data) -> 60 | Target = binary_to_list(iolist_to_binary(Data)). 61 | -------------------------------------------------------------------------------- /string-join.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% In Erlang you generally don't want to flatten lists to create a 4 | %%% pleasant looking "string" - iolists typically work fine with 5 | %%% string processing functions in the core library. 6 | %%% 7 | %%% But what happens when you want to compare a couple strings for 8 | %%% equivalence? Looking over the lists and strings modules, there 9 | %%% doesn't appear to be a function for this. Assuming there isn't a 10 | %%% better way to compare two strings, we need to ensure that both are 11 | %%% flattened. 12 | %%% 13 | %%% Typical results on my laptop under R16B: 14 | %%% 15 | %%% flatten: 823 16 | %%% concat: 429 17 | %%% append: 300 18 | %%% to_binary: 554 19 | %%% 20 | %%% These results superficially agree with this: 21 | %%% 22 | %%% http://fdmanana.wordpress.com/2010/09/02/list-concatenation-in-erlang/ 23 | %%% 24 | -mode(compile). 25 | 26 | -include("bench.hrl"). 27 | 28 | -define(TRIALS, 1000000). 29 | -define(TARGET, "sr0hZUwaVPJETArtq//HQx0YbX4ma3lcuCxzBH4UkGY2yNXz"). 30 | -define(MATCH, ["sr0hZUwaVPJ", "ETArtq//HQx0Y", "bX4ma3lcuC", 31 | "xzBH4UkGY2yNXz"]). 32 | 33 | main(_) -> 34 | test_flatten(), 35 | test_concat(), 36 | test_append(), 37 | test_to_binary(). 38 | 39 | test_flatten() -> 40 | bench("flatten", fun() -> flatten_cmp(?TARGET, ?MATCH) end, ?TRIALS). 41 | 42 | flatten_cmp(Target, Match) -> 43 | Target = lists:flatten(Match). 44 | 45 | test_concat() -> 46 | bench("concat", fun() -> concat_cmp(?TARGET, ?MATCH) end, ?TRIALS). 47 | 48 | concat_cmp(Target, Match) -> 49 | Target = lists:concat(Match). 50 | 51 | test_append() -> 52 | bench("append", fun() -> append_cmp(?TARGET, ?MATCH) end, ?TRIALS). 53 | 54 | append_cmp(Target, Match) -> 55 | Target = lists:append(Match). 56 | 57 | test_to_binary() -> 58 | bench("to_binary", fun() -> to_binary_cmp(?TARGET, ?MATCH) end, ?TRIALS). 59 | 60 | to_binary_cmp(Target, Match) -> 61 | TargetBin = iolist_to_binary(Target), 62 | MatchBin = iolist_to_binary(Match), 63 | TargetBin = MatchBin. 64 | -------------------------------------------------------------------------------- /strip-binary.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%% 3 | %%% It's pretty common to trim spaces (or other characters) from a 4 | %%% string. But what if the string is represented by a binary? Does 5 | %%% it make sense to convert the binary to a list and apply 6 | %%% string:strip to it? Or is there a better way? 7 | %%% 8 | %%% The alternative explored here is taken from this informative 9 | %%% thread: 10 | %%% 11 | %%% http://erlang.org/pipermail/erlang-questions/2009-June/044786.html 12 | %%% 13 | %%% Representative of my results (laptop): 14 | %%% 15 | %%% string_strip: 0.724 us (1381816.67 per second) 16 | %%% re_strip: 5.204 us (192149.17 per second) 17 | %%% compiled_re_strip: 4.244 us (235607.28 per second) 18 | %%% 19 | %%% So stupid is apparently a lot faster! 20 | %%% 21 | -mode(compile). 22 | 23 | -include("bench.hrl"). 24 | 25 | -define(STRING, <<" some_value_we_want_stripped ">>). 26 | -define(STRIPPED, <<"some_value_we_want_stripped">>). 27 | -define(STRIP_RE, "^\\s+|\\s+$"). 28 | -define(TRIALS, 1000000). 29 | 30 | main(_) -> 31 | test_string_strip(), 32 | test_re_strip(), 33 | test_compiled_re_strip(). 34 | 35 | test_string_strip() -> 36 | bench( 37 | "string_strip", 38 | fun() -> string_strip(?STRING) end, 39 | ?TRIALS). 40 | 41 | string_strip(Str) -> 42 | ?STRIPPED = list_to_binary(string:strip(binary_to_list(Str))). 43 | 44 | test_re_strip() -> 45 | bench( 46 | "re_strip", 47 | fun() -> re_strip(?STRING, ?STRIP_RE) end, 48 | ?TRIALS). 49 | 50 | re_strip(Str, RE) -> 51 | ?STRIPPED = re:replace(Str, RE, "", [global, {return, binary}]). 52 | 53 | test_compiled_re_strip() -> 54 | {ok, Compiled} = re:compile(?STRIP_RE), 55 | bench( 56 | "compiled_re_strip", 57 | fun() -> re_strip(?STRING, Compiled) end, 58 | ?TRIALS). 59 | --------------------------------------------------------------------------------