Skip to content

Instantly share code, notes, and snippets.

@w495
Created June 13, 2012 15:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save w495/2924882 to your computer and use it in GitHub Desktop.
Save w495/2924882 to your computer and use it in GitHub Desktop.
Тестирование скорости конкатенации
%%
%% @file test_concat.erl тестирование скорости конкатенации
%% Тестирование проводилось на списках и binary.
%% Тестировалось наивная конкатенация через:
%% * ++, для списков;
%% * <<X/binary,Y/binary>>>, для binary;
%% * вложенные списки (правильные и неправильные), cм ниже;
%% * конкатенация через lists:append, для списков;
%% * конкатенация через string:join, для списков;
%%
%% Мораль тестирования в том, что, лучше конкатенацию списков
%% и binary делать через вложенные списки,
%% без дополнительных преобразований.
%% Если при этом требуется получить вид результирующей сущности,
%% то лучше пользоваться erlang:list_to_binary, а не lists:flatten.
%% Если требуется сразу получить результат конкатенации списков,
%% то лучше пользоваться наивным ++ или lists:append или string:join,
%% в зависимости от нужд. По тестам получилось, что ++ несколько
%% быстрее, чем последние два.
%% Если требуется сразу получить результат конкатенации binary,
%% то лучше пользоваться <<X/binary,Y/binary>>>.
%%
% ============================================================================
% Erlang R15B (erts-5.9)
% [source]
% [64-bit]
% [smp:2:2]
% [async-threads:0]
% [hipe]
% [kernel-poll:false]
% ============================================================================
%
% lists
% -----------------------------------------------------------------------------------------------------------------
% |time[mks] | memory size in bytes | times | * name
% |----------|----------|----------|----------|----------|----------|----------|-----------------------------------
% | | total | proc | procu | sys | bin | |
% |----------|----------|----------|----------|----------|----------|----------|-----------------------------------
% | 3450039 | 10885456 | 1548666 | 1550186 | 9337734 | 667712 | 100000 | * item :: erlang:'++'
% | 6823747 | 10892384 | 1548675 | 1550186 | 9343501 | 673272 | 100000 | * item :: ++ erlang:list_to_binary
% | 37616 | 10536104 | 1201419 | 1200610 | 9338389 | 668032 | 100000 | * nested list :: [X,Y]
% | 36949 | 10536256 | 1201419 | 1200610 | 9338541 | 668184 | 100000 | * deep list :: [X|Y]
% | 18128505 | 10880064 | 1541578 | 1541578 | 9338358 | 668336 | 100000 | * nested list :: lists:flatten
% | 18011393 | 10880224 | 1541578 | 1541578 | 9338902 | 668496 | 100000 | * deep list :: lists:flatten
% | 5035014 | 10469616 | 1123570 | 1122770 | 9339062 | 668656 | 100000 | * nested list :: erlang:list_to_binary
% | 5036683 | 10469784 | 1123570 | 1122770 | 9339230 | 668824 | 100000 | * deep list :: erlang:list_to_binary
% | 3558985 | 10831464 | 1494642 | 1496162 | 9339142 | 668992 | 100000 | * item :: lists:append
% | 3648410 | 10838784 | 1497090 | 1497090 | 9339294 | 669144 | 100000 | * item :: string:join([X ... ], [])
% -----------------------------------------------------------------------------------------------------------------
%
% binary
% -----------------------------------------------------------------------------------------------------------------
% |time[mks] | memory size in bytes | times | * name
% |----------|----------|----------|----------|----------|----------|----------|-----------------------------------
% | | total | proc | procu | sys | bin | |
% |----------|----------|----------|----------|----------|----------|----------|-----------------------------------
% | 245128 | 10501824 | 1144267 | 1143458 | 9339853 | 669368 | 100000 | * item :: <<X/binary>>
% | 36630 | 10488336 | 1152163 | 1151354 | 9339877 | 669520 | 100000 | * nested list :: [X,Y]
% | 36393 | 10488488 | 1152163 | 1151354 | 9340157 | 669672 | 100000 | * deep list :: [X|Y]
% | 2397030 | 10571112 | 1234386 | 1234386 | 9340102 | 669824 | 100000 | * nested list :: lists:flatten
% | 2421131 | 10571400 | 1234386 | 1234386 | 9340262 | 669984 | 100000 | * deep list :: lists:flatten
% | 1220187 | 10508240 | 1157043 | 1156234 | 9337525 | 667168 | 100000 | * nested list :: erlang:list_to_binary
% | 1267500 | 10508160 | 1169810 | 1171330 | 9337326 | 667176 | 100000 | * deep list :: erlang:list_to_binary
% -----------------------------------------------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(test_concat).
-vsn(2.0).
-export([
test/0,
test/1
]).
-spec test() -> ok.
-spec test({
Select ::string(),
All ::string(),
From ::string(),
Btable ::string(),
Where ::string(),
Id ::string(),
Order ::string(),
Limit ::string()
}) -> ok.
test()->
tests:test({
" select ",
" * ",
" from ",
" Btable ",
" where ",
" id > ? ",
" order by id ",
" limit 100000 "
}),
ok.
%%
%% @spec test_concat({
%% string, string, string, string, string, string, string
%% })-> ok.
%%
test(Params)->
io:format("lists~n"),
test_concat(Params, lists),
io:format("~nbinary~n"),
test_concat(
erlang:list_to_tuple([
erlang:list_to_binary(X)||X<-erlang:tuple_to_list(Params)
]),
binary
),
io:format("~n"),
ok.
%%
%% @spec test_concat(Params::{
%% string, string, string, string, string, string, string
%% },
%% Type::lists | binary
%% )-> ok.
%%
%% @doc Основаня функция тестирования.
%% Ключевая особенность --- строки надо передавать как параметры.
%% Это нужно для того, чтобы компилятор, не проводил оптимизацию.
%% В противном случае тест будет не очень честным.
%% В зависимости от параметра Type тестирование проводится для
%% списков или binary
%%
test_concat(Params, Type)->
Times = 100000,
%%
%% Наивная конкатенация списков и binary
%%
case Type of
lists ->
print_speed("item :: erlang:'++' ",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Проверяем скорость формирования списка
add_lists({
Select,All,From,Btable,Where,Id,Order,Limit
})
end, Times, Params),
print_speed("item :: ++ erlang:list_to_binary ",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Проверяем скорость формирования списка,
%% и binary из него
List = add_lists({
Select,All,From,Btable,Where,Id,Order,Limit
}),
erlang:list_to_binary(List)
end, Times, Params);
_ ->
print_speed("item :: <<X/binary>> ",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Проверяем скорость формирования binary,
add_binary({
Select,All,From,Btable,Where,Id,Order,Limit
})
end, Times, Params)
end,
%%
%% Конкатенация на основе вложенных списков
%%
print_speed("nested list :: [X,Y]",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Правильный список
make_nested({
Select,All,From,Btable,Where,Id,Order,Limit
})
end, Times, Params),
print_speed("deep list :: [X|Y]",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Неправильный список
make_deep({
Select,All,From,Btable,Where,Id,Order,Limit
})
end, Times, Params),
%%
%% Конкатенация на основе вложенных списков со спрямлением
%% через lists:flatten
%%
print_speed("nested list :: lists:flatten",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Правильный список
Nested_list =make_nested({
Select,All,From,Btable,Where,Id,Order,Limit
}),
lists:flatten(Nested_list)
end, Times, Params),
print_speed("deep list :: lists:flatten",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Неправильный список
Deep_list = make_deep({
Select,All,From,Btable,Where,Id,Order,Limit
}),
lists:flatten(Deep_list)
end, Times, Params),
%%
%% Конкатенация на основе вложенных списков со спрямлением
%% через приведение к binary
%%
print_speed("nested list :: erlang:list_to_binary",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Правильный список
Nested_list =make_nested({
Select,All,From,Btable,Where,Id,Order,Limit
}),
erlang:list_to_binary(Nested_list)
end, Times, Params),
print_speed("deep list :: erlang:list_to_binary",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% Неправильный список
Deep_list = make_deep({
Select,All,From,Btable,Where,Id,Order,Limit
}),
erlang:list_to_binary(Deep_list)
end, Times, Params),
%%
%% Конкатенация на основе сложения списков
%%
case Type of
lists ->
print_speed("item :: lists:append ",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% lists:append реадизован через ++
append_lists({
Select,All,From,Btable,Where,Id,Order,Limit
})
end, Times, Params),
print_speed(" item :: string:join([X ... ], [])",
fun({Select, All, From, Btable, Where, Id, Order, Limit}) ->
%% string:join реадизован через ++
join_lists({
Select,All,From,Btable,Where,Id,Order,Limit
})
end, Times, Params);
_ ->
ok
end,
ok.
%% @spec append_lists({
%% string, string, string, string, string, string, string
%% })-> string.
%%
%% @doc Возвращает список полученный путем конкатенации
%% списков через lists:append
%%
append_lists({Select, All, From, Btable, Where, Id, Order, Limit})->
X = lists:append([Select, All, From, Btable, Where, Id, Order, Limit]),
Y = lists:append([X,X,X,X]),
Z = lists:append([Y,Y,Y,Y]),
lists:append([Z,Z,Z,Z,Z]).
%% @spec join_lists({
%% string, string, string, string, string, string, string
%% })-> string.
%%
%% @doc Возвращает список полученный путем конкатенации
%% списков через string:join
%%
join_lists({Select, All, From, Btable, Where, Id, Order, Limit})->
X = string:join([Select, All, From, Btable, Where, Id, Order, Limit], []),
Y = string:join([X,X,X,X], []),
Z = string:join([Y,Y,Y,Y], []),
string:join([Z,Z,Z,Z,Z], []).
%% @spec add_lists({
%% string, string, string, string, string, string, string
%% })-> string.
%%
%% @doc Возвращает список полученный путем списков через оператор ++
%%
add_lists({Select, All, From, Btable, Where, Id, Order, Limit})->
X = Select ++ All ++ From ++ Btable
++ Where ++ Id ++ Order ++ Limit,
Y = X ++ X ++ X ++ X,
Z = Y ++ Y ++ Y ++ Y,
Z ++ Z ++ Z ++ Z ++ Z.
%% @spec add_binary({
%% binary, binary, binary, binary, binary, binary, binary
%% })-> binary.
%%
%% @doc Возвращает binary полученный путем сложения binary
%%
add_binary({Select, All, From, Btable, Where, Id, Order, Limit})->
X = <<Select/binary,All/binary,From/binary,Btable/binary,
Where/binary, Id/binary, Order/binary, Limit/binary>>,
Y = <<X/binary, X/binary,X/binary,X/binary>>,
Z = <<Y/binary,Y/binary, Y/binary, Y/binary>>,
<<Z/binary, Z/binary,Z/binary,Z/binary,Z/binary>>.
%% @spec make_deep({
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary
%% })-> improper_list(string|binary, string|binary) .
%%
%% @doc Возвращает не правильный вложенный список.
%% Каждый новый элемент списка добавляется к хвосту списка,
%% а хвост тоже элемент.
%%
make_deep({Select, All, From, Btable, Where, Id, Order, Limit})->
X = [Select|[All|[From|[Btable|[Where|[Id|[Order|[Limit]]]]]]]],
Y = [X|[X|[X|[X]]]],
Z = [Y|[Y|[Y|[Y]]]],
[Z|[Z|[Z|[Z|[Z]]]]].
%% @spec make_nested({
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary,
%% string|binary
%% })-> list(string|binary) .
%%
%% @doc Возвращает правильный вложенный список
%%
make_nested({Select, All, From, Btable, Where, Id, Order, Limit})->
X = [Select, All, From, Btable, Where, Id, Order, Limit],
Y = [X, X, X, X],
Z = [Y, Y, Y, Y],
[Z, Z, Z, Z, Z].
%% @spec print_speed({
%% string
%% (fun(any) -> any),
%% integer,
%% any,
%% })-> ok
%%
%% @doc Вызывает функцию Fun с параметром Params Times раз.
%% Печатает информацию о ее выполнении:
%% 1) время выполнения;
%% 2) erlang:memory(total),
%% 3) erlang:memory('processes'),
%% 4) erlang:memory(processes_used),
%% 5) erlang:memory(system),
%% 6) erlang:memory(binary),
%% 7) количество раз (Times)
%% 8) имя теста Name
%%
print_speed(Name, Fun, Times, Params) ->
Start = utime(),
erlang:garbage_collect(),
iter(Times, Fun, Params),
Stop = utime(),
io:format(
%"| TIME | memory | times| name
%"| | total | proc | procu | sys | bin | |
"| ~8.B | ~8.B | ~8.B | ~8.B | ~8.B | ~8.B | ~8.B | * ~s ~n",[
Stop - Start,
erlang:memory(total),
erlang:memory('processes'),
erlang:memory(processes_used),
erlang:memory(system),
erlang:memory(binary),
Times, Name
]
).
%% @spec iter({
%% integer
%% (fun(any) -> any),
%% any,
%% })-> ok
%% @doc Вызывает функцию Fun с параметром Params Times раз.
%%
iter(0, _, _) ->
ok;
iter(Times, F, Params) ->
erlang:apply(F, [Params]),
iter(Times-1, F, Params).
%% @spec utime() -> integer
%%
%% @doc Возвращает текущее количество микросекунд.
%%
utime() ->
{_M,S,Mi} = now(), S*1000000+Mi.
@w495
Copy link
Author

w495 commented Jul 26, 2012

Предыдущая версия передавала строки не через параметры и компилятор оптимизировал конкатенацию. Можно считать результаты предыдущей версии ошибочными.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment