diff --git a/bench/ets_fixed_elements/Makefile b/bench/ets_fixed_elements/Makefile new file mode 100644 index 0000000..5e4c889 --- /dev/null +++ b/bench/ets_fixed_elements/Makefile @@ -0,0 +1,25 @@ +ERLC_OPTS = + +SRCD = src +EBIND = ebin + +ERLF = $(wildcard $(SRCD)/*.erl) +BEAMF = $(patsubst $(SRCD)/%.erl,$(EBIND)/%.beam,$(ERLF)) + +.PHONY: all bench clean + +all: bench + +bench: $(BEAMF) + +$(EBIND)/%.beam: $(SRCD)/%.erl + erlc $(ERLC_OPTS) -o$(EBIND) $< + +$(BEAMF): | $(EBIND) + +$(EBIND): + mkdir -p $(EBIND) + +clean: + $(RM) -rf $(EBIND) + diff --git a/bench/ets_fixed_elements/src/ets_fixed_elements.erl b/bench/ets_fixed_elements/src/ets_fixed_elements.erl new file mode 100644 index 0000000..47d20f4 --- /dev/null +++ b/bench/ets_fixed_elements/src/ets_fixed_elements.erl @@ -0,0 +1,81 @@ +-module(ets_fixed_elements). + +-export([bench_args/2, run/3]). + + +%% Description: Inserts small number of fixed keys many times into ets +%% table in parallel. It test ets with set table type and write and +%% read concurrency enabled; and etsmp. See etsmp.erl for a +%% description of etsmp. +%% +%% Authors: David Klaftenegger (david.klaftenegger@it.uu.se) and +%% Kjell Winblad (kjell.winblad@it.uu.se) + + +bench_args(Version, _) -> + NrOfOperations = + case Version of + short -> 500000; + intermediate -> 5000000; + long -> 50000000 + end, + TableImpels = [ets, etsmp], + [[NrOfOperations, TableImpel] || TableImpel <- TableImpels]. + + +run([NrOfOperations, TableImpel| _], _, _) -> + NrOfWorkers = erlang:system_info(schedulers), + par_insert(NrOfOperations, 1024, NrOfWorkers, TableImpel). + + +%% Count = how many inserts +%% Max = maximum number of DIFFERENT inserts +%% WorkerCount = number of parallel inserts +par_insert(Count, Max, WorkerCount, TableImpel) -> + {Table, Workers} = setup(Count, Max, WorkerCount, TableImpel), + par_insert(Workers), + TableImpel:delete(Table), + ok. + +%% start workers and wait for workers to finish +par_insert(Workers) -> + par_insert(Workers, []). +par_insert([], Running) -> + wait_for(Running); +par_insert([Next | Waiting], Running) -> + Next ! worker_start, + par_insert(Waiting, [Next | Running]). + +%% wait for start signal, insert into table & notify parent when done +insert_and_msg(Start, End, Max, Table, Parent, TableImpel) -> + random:seed(now()), + Src = array:from_list([X||{_,X} <- lists:sort([ {random:uniform(), N} || N <- lists:seq(0,Max+1)])]), + receive worker_start -> ok end, + insert(Start, End, Max, Table, Src, TableImpel), + Parent ! {self(), worker_done}, + ok. + +wait_for([]) -> ok; +wait_for([Worker | Workers]) -> + receive {Worker, worker_done} -> ok end, + wait_for(Workers), + ok. + +insert(Count, Count, _Max, _Table, _Src,_) -> ok; +insert(Num, Count, Max, Table, Src, TableImpel) -> + TableImpel:insert(Table, {array:get((Num rem Max)+1, Src), ignored}), + insert(Num+1, Count, Max, Table, Src, TableImpel). + +setup(Count, Max, WorkerCount, TableImpel) -> + Table = TableImpel:new(?MODULE, [set, public, {read_concurrency, true}, {write_concurrency, true}]), + setup(Count, Max, WorkerCount, Table, 0, [], TableImpel). + +setup(_Count, _Max, _WorkerCount, Table, _WorkerCount, Workers, _TableImpel) -> + {Table, Workers}; +setup(Count, Max, WorkerCount, Table, Started, Workers, TableImpel) -> + MainProcess = self(), + Range = Count div WorkerCount, + Start = Started*Range, + End = Start+Range, + Workers2 = [spawn(fun() -> insert_and_msg(Start, End, Max, Table, MainProcess, TableImpel) end) | Workers], + setup(Count, Max, WorkerCount, Table, Started+1, Workers2, TableImpel). diff --git a/bench/ets_fixed_elements/src/etsmp.erl b/bench/ets_fixed_elements/src/etsmp.erl new file mode 100644 index 0000000..c6b4429 --- /dev/null +++ b/bench/ets_fixed_elements/src/etsmp.erl @@ -0,0 +1,99 @@ +-module(etsmp). + +-compile(export_all). + +new(_,_) -> + new(). + +new() -> + NrOfSchedulers = erlang:system_info(schedulers), + NrOfSubTables = prime_greater_or_equal_than(NrOfSchedulers), + AllocateETSFun = + fun() -> + ets:new(test_table, + [set, + public, + {write_concurrency,true}, + {read_concurrency,true}]) + end, + lists:foldl(fun(Index, Array) -> + array:set(Index, AllocateETSFun(), Array) + end, + array:new(NrOfSubTables), + lists:seq(0, NrOfSubTables -1)). + +delete(EtsmID) -> + lists:foreach(fun(EtsID) -> + ets:delete(EtsID) + end, + array:to_list(EtsmID)). + +insert(EtsmID, Tuple) -> + HashValue = erlang:phash2(element(1,Tuple), array:size(EtsmID)), + SubTable = array:get(HashValue, EtsmID), + ets:insert(SubTable, Tuple). + +delete(EtsmID, Key) -> + HashValue = erlang:phash2(Key, array:size(EtsmID)), + SubTable = array:get(HashValue, EtsmID), + ets:delete(SubTable, Key). + +lookup(EtsmID, Key) -> + HashValue = erlang:phash2(Key, array:size(EtsmID)), + SubTable = array:get(HashValue, EtsmID), + ets:lookup(SubTable, Key). + + +prime_greater_or_equal_than(Number) -> + case Number =< 2 of + true -> + 2; + false -> + prime_greater_or_equal_than(Number, 3, [2]) + end. + +prime_greater_or_equal_than(Number, NextNumber, PrimesSoFar) -> + IsPrime = + lists:all( + fun(Prime) -> + (NextNumber rem Prime) =/= 0 + end, + PrimesSoFar), + case IsPrime of + true -> + case Number =< NextNumber of + true -> + NextNumber; + false -> + prime_greater_or_equal_than( + Number, + NextNumber + 1, + PrimesSoFar ++ [NextNumber]) + end; + false -> + prime_greater_or_equal_than( + Number, + NextNumber + 1, + PrimesSoFar) + end. + + + + + +test() -> + EtsmID = new(), + insert(EtsmID, {1}), + insert(EtsmID, {2}), + insert(EtsmID, {3}), + [{1}] = lookup(EtsmID, 1), + [{2}] = lookup(EtsmID, 2), + [{3}] = lookup(EtsmID, 3), + delete(EtsmID, 1), + delete(EtsmID, 2), + delete(EtsmID, 3), + [] = lookup(EtsmID, 1), + [] = lookup(EtsmID, 2), + [] = lookup(EtsmID, 3), + delete(EtsmID), + ok.