blob: fab1e8d4432195c064aef59754e7b7a09c94f829 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import json
from archery.benchmark.codec import JsonEncoder
from archery.benchmark.core import Benchmark, median
from archery.benchmark.compare import (
BenchmarkComparator, RunnerComparator
)
from archery.benchmark.google import (
GoogleBenchmark, GoogleBenchmarkObservation
)
from archery.benchmark.runner import StaticBenchmarkRunner
def test_benchmark_comparator():
unit = "micros"
assert not BenchmarkComparator(
Benchmark("contender", unit, True, [10], unit, [1]),
Benchmark("baseline", unit, True, [20], unit, [1]),
).regression
assert BenchmarkComparator(
Benchmark("contender", unit, False, [10], unit, [1]),
Benchmark("baseline", unit, False, [20], unit, [1]),
).regression
assert BenchmarkComparator(
Benchmark("contender", unit, True, [20], unit, [1]),
Benchmark("baseline", unit, True, [10], unit, [1]),
).regression
assert not BenchmarkComparator(
Benchmark("contender", unit, False, [20], unit, [1]),
Benchmark("baseline", unit, False, [10], unit, [1]),
).regression
def test_static_runner_from_json_not_a_regression():
archery_result = {
"suites": [
{
"name": "arrow-value-parsing-benchmark",
"benchmarks": [
{
"name": "FloatParsing<DoubleType>",
"unit": "items_per_second",
"less_is_better": False,
"values": [
109941112.87296811
],
"time_unit": "ns",
"times": [
9095.800104330105
]
},
]
}
]
}
contender = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
baseline = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
[comparison] = RunnerComparator(contender, baseline).comparisons
assert not comparison.regression
def test_static_runner_from_json_regression():
archery_result = {
"suites": [
{
"name": "arrow-value-parsing-benchmark",
"benchmarks": [
{
"name": "FloatParsing<DoubleType>",
"unit": "items_per_second",
"less_is_better": False,
"values": [
109941112.87296811
],
"time_unit": "ns",
"times": [
9095.800104330105
]
},
]
}
]
}
contender = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
# introduce artificial regression
archery_result['suites'][0]['benchmarks'][0]['values'][0] *= 2
baseline = StaticBenchmarkRunner.from_json(json.dumps(archery_result))
[comparison] = RunnerComparator(contender, baseline).comparisons
assert comparison.regression
def test_benchmark_median():
assert median([10]) == 10
assert median([1, 2, 3]) == 2
assert median([1, 2]) == 1.5
assert median([1, 2, 3, 4]) == 2.5
assert median([1, 1, 1, 1]) == 1
try:
median([])
assert False
except ValueError:
pass
def assert_benchmark(name, google_result, archery_result):
observation = GoogleBenchmarkObservation(**google_result)
benchmark = GoogleBenchmark(name, [observation])
result = json.dumps(benchmark, cls=JsonEncoder)
assert json.loads(result) == archery_result
def test_items_per_second():
name = "ArrayArrayKernel<AddChecked, UInt8Type>/32768/0"
google_result = {
"cpu_time": 116292.58886653671,
"items_per_second": 281772039.9844759,
"iterations": 5964,
"name": name,
"null_percent": 0.0,
"real_time": 119811.77313729875,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"size": 32768.0,
"threads": 1,
"time_unit": "ns",
}
archery_result = {
"counters": {"iterations": 5964,
"null_percent": 0.0,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "items_per_second",
"less_is_better": False,
"values": [281772039.9844759],
"time_unit": "ns",
"times": [119811.77313729875],
}
assert "items_per_second" in google_result
assert "bytes_per_second" not in google_result
assert_benchmark(name, google_result, archery_result)
def test_bytes_per_second():
name = "BufferOutputStreamLargeWrites/real_time"
google_result = {
"bytes_per_second": 1890209037.3405428,
"cpu_time": 17018127.659574457,
"iterations": 47,
"name": name,
"real_time": 17458386.53190963,
"repetition_index": 1,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"threads": 1,
"time_unit": "ns",
}
archery_result = {
"counters": {"iterations": 47,
"repetition_index": 1,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "bytes_per_second",
"less_is_better": False,
"values": [1890209037.3405428],
"time_unit": "ns",
"times": [17458386.53190963],
}
assert "items_per_second" not in google_result
assert "bytes_per_second" in google_result
assert_benchmark(name, google_result, archery_result)
def test_both_items_and_bytes_per_second():
name = "ArrayArrayKernel<AddChecked, UInt8Type>/32768/0"
google_result = {
"bytes_per_second": 281772039.9844759,
"cpu_time": 116292.58886653671,
"items_per_second": 281772039.9844759,
"iterations": 5964,
"name": name,
"null_percent": 0.0,
"real_time": 119811.77313729875,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"size": 32768.0,
"threads": 1,
"time_unit": "ns",
}
# Note that bytes_per_second trumps items_per_second
archery_result = {
"counters": {"iterations": 5964,
"null_percent": 0.0,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "bytes_per_second",
"less_is_better": False,
"values": [281772039.9844759],
"time_unit": "ns",
"times": [119811.77313729875],
}
assert "items_per_second" in google_result
assert "bytes_per_second" in google_result
assert_benchmark(name, google_result, archery_result)
def test_neither_items_nor_bytes_per_second():
name = "AllocateDeallocate<Jemalloc>/size:1048576/real_time"
google_result = {
"cpu_time": 1778.6004847419827,
"iterations": 352765,
"name": name,
"real_time": 1835.3137357788837,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"threads": 1,
"time_unit": "ns",
}
archery_result = {
"counters": {"iterations": 352765,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "ns",
"less_is_better": True,
"values": [1835.3137357788837],
"time_unit": "ns",
"times": [1835.3137357788837],
}
assert "items_per_second" not in google_result
assert "bytes_per_second" not in google_result
assert_benchmark(name, google_result, archery_result)
def test_prefer_real_time():
name = "AllocateDeallocate<Jemalloc>/size:1048576/real_time"
google_result = {
"cpu_time": 1778.6004847419827,
"iterations": 352765,
"name": name,
"real_time": 1835.3137357788837,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"threads": 1,
"time_unit": "ns",
}
archery_result = {
"counters": {"iterations": 352765,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "ns",
"less_is_better": True,
"values": [1835.3137357788837],
"time_unit": "ns",
"times": [1835.3137357788837],
}
assert name.endswith("/real_time")
assert_benchmark(name, google_result, archery_result)
def test_prefer_cpu_time():
name = "AllocateDeallocate<Jemalloc>/size:1048576"
google_result = {
"cpu_time": 1778.6004847419827,
"iterations": 352765,
"name": name,
"real_time": 1835.3137357788837,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"threads": 1,
"time_unit": "ns",
}
archery_result = {
"counters": {"iterations": 352765,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "ns",
"less_is_better": True,
"values": [1778.6004847419827],
"time_unit": "ns",
"times": [1835.3137357788837],
}
assert not name.endswith("/real_time")
assert_benchmark(name, google_result, archery_result)
def test_omits_aggregates():
name = "AllocateDeallocate<Jemalloc>/size:1048576/real_time"
google_aggregate = {
"aggregate_name": "mean",
"cpu_time": 1757.428694267678,
"iterations": 3,
"name": "AllocateDeallocate<Jemalloc>/size:1048576/real_time_mean",
"real_time": 1849.3869337041162,
"repetitions": 0,
"run_name": name,
"run_type": "aggregate",
"threads": 1,
"time_unit": "ns",
}
google_result = {
"cpu_time": 1778.6004847419827,
"iterations": 352765,
"name": name,
"real_time": 1835.3137357788837,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"run_type": "iteration",
"threads": 1,
"time_unit": "ns",
}
archery_result = {
"counters": {"iterations": 352765,
"repetition_index": 0,
"repetitions": 0,
"run_name": name,
"threads": 1},
"name": name,
"unit": "ns",
"less_is_better": True,
"values": [1835.3137357788837],
"time_unit": "ns",
"times": [1835.3137357788837],
}
assert google_aggregate["run_type"] == "aggregate"
assert google_result["run_type"] == "iteration"
observation1 = GoogleBenchmarkObservation(**google_aggregate)
observation2 = GoogleBenchmarkObservation(**google_result)
benchmark = GoogleBenchmark(name, [observation1, observation2])
result = json.dumps(benchmark, cls=JsonEncoder)
assert json.loads(result) == archery_result