Browse Source

added performance tests

pull/2/head
Sean Copenhaver 11 years ago
parent
commit
a34dc58beb
  1. 10
      Makefile
  2. 20
      test/fixtures/hello_ground.txt
  3. 173
      test/perf_test.exs
  4. 2
      test/rope_test.exs

10
Makefile

@ -1,16 +1,16 @@
.PHONY: test docs
.PHONY: test docs tags
default: ctags test
default: tags test
all: ctags test dialyzer docs
all: tags test check docs
test:
mix test
ctags:
tags:
ctags -R .
dialyzer:
check:
dialyzer ./ebin --fullpath --no_check_plt -Wno_return
docs:

20
test/fixtures/hello_ground.txt

@ -0,0 +1,20 @@
Another thing that got forgotten was the fact that against all probability a sperm whale had been suddenly been called into existence several miles above the surface of an alien planet.
And since this is not a naturally tenable position for a whale, this poor innocent creature had very little time to come to terms with its identity as a whale before it then had to come to terms with not being a whale any more.
This is a complete record of its thought from the moment it began its life till the moment it ended it.
Ah . . . ! What's happening? it thought.
Er, excuse me, who am I?
Hello?
Why am I here? What's my purpose in life?
What do I mean by who am I?
Oh no, not again.
Calm down, get a grip now . . . oh! this is an interesting what is it?
It's sort of . . . yawning, tingling sensation in my . . . my . . . well, I suppose I'd better start finding names for things if I want to make any headway in what for that sake of what I shall call an argument I shall call the world, so let's call it my stomach.
Good. Ooooh, it's getting quite strong. And hey, what about this whistling roaring sound going past what I'm suddenly going to call my head? Perhaps I can call that . . . wind! Is that a good name? It'll do . . . perhaps I can find a better name for it later when I've found out what it's for.
It must be something very important because there certainly seems to be a hell of a lot of it. Hey! What's this thing? This . . . let's call it a tail - yeah, tail. Hey! I can really thrash it about pretty good, can't I? Wow! Wow! That feels great! Doesn't seem to achieve very much but I'll probably find out what it's for later on. Now, have I built up any coherent picture of things yet?
No.
Never mind, hey, this is really exciting, so much to find out about, so much to look forward to, I'm quite dizzy with anticipation . . . Or is it the wind?
There really is a lot of that now, isn't there? And wow! Hey! What's this thing suddenly coming toward me very fast? Very, very fast. So big and flat and round, it needs a big wide-sounding name like . . . ow . . . ound . . . round . . . ground! That's it! That's a good name- ground!
I wonder if it will be friends with me?
Hello Ground!

173
test/perf_test.exs

@ -0,0 +1,173 @@
defmodule PerfTest do
use ExUnit.Case
defrecord TestCtxt,
rope: nil,
extra: [],
time: 0
test "10,000 concats" do
threshold = 30_000 #30 milliseconds
time = build_rope |> build_ctxt |> run(10_000, :concat)
IO.puts "\nROPE: 10,000 concats took #{time} microseconds"
assert time < threshold, "10,000 concats completed in #{time} microseconds, longer then threshold of #{threshold} microseconds"
end
test "rebalancing 10,000 words" do
threshold = 40_000 #40 milliseconds
time = build_long_rope |> build_ctxt |> run(1, :rebalance)
IO.puts "\nROPE: 10,000 node rebalance took #{time} microseconds"
assert time < threshold, "rebalancing worst case 10,000 leaf rope completed in #{time} microseconds, longer then threshold of #{threshold} microseconds"
end
test "1,000 slices on a balanced rope" do
threshold = 100_000 #1/10 second
time = build_rope |> build_ctxt |> run(1000, :slice)
IO.puts "\nROPE: 1,000 slice took #{time} microseconds"
assert time < threshold, "1,000 slices on a balanced rope completed in #{time} microseconds, longer then threshold of #{threshold} microseconds"
end
test "100 finds on a balanced rope" do
threshold = 500_000 #1/2 second
time = build_rope |> build_ctxt |> run(100, :find)
IO.puts "\nROPE: 100 find took #{time} microseconds"
assert time < threshold, "100 finds on a balanced rope completed in #{time} microseconds, longer then threshold of #{threshold} microseconds"
end
test "string performance" do
time = build_text |> build_ctxt |> run(10_000, :concat)
IO.puts "\nSTRING: 10,000 concats took #{time} microseconds"
time = build_text |> build_ctxt |> run(1_000, :slice)
IO.puts "\nSTRING: 1,000 slices took #{time} microseconds"
time = build_text |> build_ctxt |> run(100, :find)
IO.puts "\nSTRING: 100 contains? took #{time} microseconds"
end
def build_ctxt(rope) do
TestCtxt[rope: rope, extra: build_extra]
end
def build_rope do
File.stream!("test/fixtures/hello_ground.txt")
|> Enum.reduce("", fn(line, rope) -> Rope.concat(rope, line) end)
|> Rope.rebalance
end
def build_long_rope() do
extra = build_extra
Enum.reduce(1..10_000, "", fn(_count, left) ->
Rope.concat([left | Enum.take(extra, 1)])
end)
end
def build_extra do
File.read!("test/fixtures/towels.txt") |> String.split |> Stream.cycle
end
def build_text do
File.read!("test/fixtures/hello_ground.txt")
end
def get_timestamp do
{mega,sec,micro} = :erlang.now()
(mega*1000000+sec)*1000000+micro
end
def run(ctxt, num, op) do
finished = Enum.reduce(1..num, ctxt, fn(_count, TestCtxt[rope: rope] = ctxt) ->
operation = {op, generate_args(op, ctxt)}
time_operation(operation, ctxt)
end)
finished.time
end
def generate_args(:slice, TestCtxt[rope: rope])
when is_record(rope, Rope) do
{:random.uniform(div(rope.length, 2)), :random.uniform(rope.length) + 100}
end
def generate_args(:slice, TestCtxt[rope: text])
when is_binary(text) do
{:random.uniform(div(String.length(text), 2)), :random.uniform(String.length(text)) + 100}
end
def generate_args(:concat, TestCtxt[extra: extra]) do
[w] = Enum.take(extra, 1)
w
end
def generate_args(:find, _ctxt) do
case :random.uniform(6) do
1 -> "my"
2 -> "your"
3 -> "going"
4 -> "N/A"
5 -> "I"
6 -> "You"
end
end
def generate_args(opt, _ctxt) do
[]
end
def time_operation(operation, ctxt) do
startTime = get_timestamp
newCtxt = execute_operation(operation, ctxt)
endTime = get_timestamp
newCtxt.update_time(fn(total) -> total + (endTime - startTime) end)
end
###########################
# Rope operations
###########################
def execute_operation({:slice, {start, len}}, TestCtxt[rope: rope] = ctxt)
when is_record(rope, Rope) do
Rope.slice(rope, start, len)
ctxt #leaving the context unchanged
end
def execute_operation({:concat, word}, TestCtxt[rope: rope] = ctxt)
when is_record(rope, Rope) do
newRope = Rope.concat([rope | [word]])
ctxt.rope newRope
end
def execute_operation({:find, term}, TestCtxt[rope: rope] = ctxt)
when is_record(rope, Rope) do
Rope.find(rope, term)
ctxt #no change, and find returns the index
end
def execute_operation({:rebalance, _args}, TestCtxt[rope: rope] = ctxt) do
newRope = Rope.rebalance(rope)
ctxt.rope newRope
end
############################
# String operations for comparison
############################
def execute_operation({:slice, {start, len}}, TestCtxt[rope: rope] = ctxt)
when is_binary(rope) do
String.slice(rope, start, len)
ctxt #leaving the context unchanged
end
def execute_operation({:concat, word}, TestCtxt[rope: rope] = ctxt)
when is_binary(rope) do
newRope = rope <> word
ctxt.rope newRope
end
def execute_operation({:find, term}, TestCtxt[rope: rope] = ctxt)
when is_binary(rope) do
String.contains?(rope, term)
ctxt #no change, and find returns the index
end
end

2
test/rope_test.exs

@ -1,5 +1,3 @@
Code.require_file "test_helper.exs", __DIR__
defmodule RopeTest do
use ExUnit.Case

Loading…
Cancel
Save