Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Benchmarks #192

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ _build
.rebar
compile_commands.json
rebar3.crashdump
.rebar3/
13 changes: 12 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
BENCH_BASELINE?=baseline

compile:
@./rebar3 compile

Expand All @@ -14,4 +16,13 @@ dialyze:
xref:
./rebar3 as test xref

.PHONY: compile clean test dialyze
bench:
./rebar3 as test bench

bench-init:
./rebar3 as test bench --save-baseline $(BENCH_BASELINE)

bench-compare:
./rebar3 as test bench --baseline $(BENCH_BASELINE)

.PHONY: compile clean test dialyze bench bench-init bench-compare
20 changes: 20 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,23 @@ For that, you can add the following line to your `rebar.config` file:
{eiconv, "1.0.0"}
]}.
```

## Contribution guide

Contributions are welcome! Here's how to create a patch that's easy to integrate:

* Create a new branch from master for the proposed fix.
* Try to maintain code style that is consistent with surrounding code. gen_smtp uses
tabs for indentation. We provide [.editorconfig](.editorconfig) file.
* Make sure PR includes a test and documentation, if appropriate. `make dialyze` should pass.
* If you think your change may affect performance, run benchmarks before and after your changes
and attach results to PR.
* Travis build should pass.

### How to run benchmarks

Run `make bench-init` before making any changes. It will run all benchmarks and save results to file.
After making your changes, run `make bench-compare`. This will
run benchmarks once again and will compare results with baseline saved earlier.

See [rebar3_bench docs](https://github.com/seriyps/rebar3_bench) for more details.
3 changes: 2 additions & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@
[
{cover_enabled, true},
{cover_print_enabled, true},
{plugins, [{rebar3_bench, "0.2.0"}]},
{deps,
[
{eiconv, "1.0.0"}
]}
]}
]}.
]}.
Binary file modified rebar3
Binary file not shown.
100 changes: 100 additions & 0 deletions test/bench_mimemail.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
%% @doc Benchmarks for `mimemail' module
-module(bench_mimemail).


-export([parse_plain/1, bench_parse_plain/2,
parse_quoted_printable/1, bench_parse_quoted_printable/2,
parse_base64/1, bench_parse_base64/2,
parse_gamut/1, bench_parse_gamut/2
]).
-export([encode_plain/1, bench_encode_plain/2,
encode_bin_attach/1, bench_encode_bin_attach/2,
encode_dkim_ss/1, bench_encode_dkim_ss/2,
encode_dkim_rs/1, bench_encode_dkim_rs/2]).

%% Parsing

%% @doc benchmark plaintext email decoding
parse_plain({input, _}) ->
read_fixture("Plain-text-only.eml").

bench_parse_plain(Bin, _) ->
{_, _, _, _, _} = mimemail:decode(Bin).


%% @doc bench email with quoted-printable body
parse_quoted_printable({input, _}) ->
read_fixture("shift-jismail").

bench_parse_quoted_printable(Bin, _) ->
{_, _, _, _, _} = mimemail:decode(Bin).


%% @doc bench decoding of email with only base64 content
parse_base64({input, _}) ->
read_fixture("image-attachment-only.eml").

bench_parse_base64(Bin, _) ->
{_, _, _, _, _} = mimemail:decode(Bin).


%% @doc bench decoding of email with many different bodies
parse_gamut({input, _}) ->
read_fixture("the-gamut.eml").

bench_parse_gamut(Bin, _) ->
{_, _, _, _, _} = mimemail:decode(Bin).


%% Encoding

%% @doc bench encoding of plain email
encode_plain({input, _}) ->
mimemail:decode(read_fixture("Plain-text-only.eml")).

bench_encode_plain(Mimetuple, _) ->
mimemail:encode(Mimetuple).


%% @doc bench encoding of email with binary attachment
encode_bin_attach({input, _}) ->
mimemail:decode(read_fixture("image-attachment-only.eml")).

bench_encode_bin_attach(Mimetuple, _) ->
mimemail:encode(Mimetuple).


%% @doc bench encoding with relaxed/simple dkim signature
encode_dkim_rs({input, _}) ->
Mail = mimemail:decode(read_fixture("image-attachment-only.eml")),
PrivKey = read_fixture("dkim-rsa-private.pem"),
Opts = [{dkim, [{s, <<"foo.bar">>},
{d, <<"example.com">>},
{c, {relaxed, simple}},
{private_key, {pem_plain, PrivKey}}]}],
{Mail, Opts}.

bench_encode_dkim_rs({Mimetuple, Opts}, _) ->
mimemail:encode(Mimetuple, Opts).


%% @doc bench encoding with simple/simple dkim signature
encode_dkim_ss({input, _}) ->
Mail = mimemail:decode(read_fixture("image-attachment-only.eml")),
PrivKey = read_fixture("dkim-rsa-private.pem"),
Opts = [{dkim, [{s, <<"foo.bar">>},
{d, <<"example.com">>},
{c, {simple, simple}},
{private_key, {pem_plain, PrivKey}}]}],
{Mail, Opts}.

bench_encode_dkim_ss({Mimetuple, Opts}, _) ->
mimemail:encode(Mimetuple, Opts).


%%
%% Helpers
%%
read_fixture(Name) ->
{ok, B} = file:read_file(filename:join(["test", "fixtures", Name])),
B.
28 changes: 28 additions & 0 deletions test/bench_session.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
%% @doc Benchmark for full SMTP session
-module(bench_session).

-export([session/1,
bench_session/2]).

%% @doc benchmark to test SMTP session performance
%% Doesn't include TCP setup time. Only single SMTP mail delivery session.
session(init) ->
Port = 9876,
gen_smtp_application:ensure_all_started(gen_smtp),
{ok, _ServerPid} = gen_smtp_server:start(
dummy_smtp_handler,
[{domain, "localhost"},
{port, Port}]),
{ok, Cli} = gen_smtp_client:open([{relay, "localhost"}, {port, Port}]),
Cli;
session({stop, Cli}) ->
ok = gen_smtp_client:close(Cli),
gen_smtp_server:stop(gen_smtp_server),
ok;
session({input, _St}) ->
{<<"[email protected]">>,
["<[email protected]>"],
binary:copy(<<0>>, 512 * 1024)}.

bench_session(Mail, Cli) ->
{ok, _} = gen_smtp_client:deliver(Cli, Mail).
60 changes: 60 additions & 0 deletions test/dummy_smtp_handler.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
%% Dummy SMTP session handler. It does absolutely nothing.
-module(dummy_smtp_handler).
-behaviour(gen_smtp_server_session).


-export([init/4, handle_HELO/2, handle_EHLO/3, handle_MAIL/2, handle_MAIL_extension/2,
handle_RCPT/2, handle_RCPT_extension/2, handle_DATA/4, handle_RSET/1, handle_VRFY/2,
handle_other/3, handle_AUTH/4, handle_STARTTLS/1, handle_info/2,
code_change/3, terminate/2]).

init(_Hostname, _SessionCount, _Address, _Options) ->
State = [],
{ok, <<"hi">>, State}.

handle_HELO(_Hostname, State) ->
{ok, 10 * 1024 * 1024, State}. % 10mb

handle_EHLO(_Hostname, Extensions, State) ->
{ok, Extensions, State}.

handle_MAIL(_From, State) ->
{ok, State}.

handle_MAIL_extension(_Extension, _State) ->
error.

handle_RCPT(_To, State) ->
{ok, State}.

handle_RCPT_extension(_Extension, _State) ->
error.

handle_DATA(_From, _To, _Data, State) ->
%% some kind of unique id
Reference = erlang:integer_to_binary(erlang:unique_integer()),
{ok, Reference, State}.

handle_RSET(State) ->
State.

handle_VRFY(_Address, State) ->
{error, "252 VRFY disabled by policy, just send some mail", State}.

handle_other(Verb, _Args, State) ->
{["500 Error: command not recognized : '", Verb, "'"], State}.

handle_AUTH(_Type, _Username, _Password, _State) ->
error.

handle_STARTTLS(State) ->
State.

handle_info(_Info, State) ->
{noreply, State}.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

terminate(Reason, State) ->
{ok, Reason, State}.