From 5281a430ed08d278b2bb469d38672aceb52f80d9 Mon Sep 17 00:00:00 2001 From: ramin Date: Thu, 5 Jul 2018 00:41:03 +0200 Subject: [PATCH 1/9] pytest.benchmark --- benchmark/benchmark.py | 381 +---------------------------------- benchmark/benchmark_ujson.py | 376 ++++++++++++++++++++++++++++++++++ 2 files changed, 386 insertions(+), 371 deletions(-) create mode 100644 benchmark/benchmark_ujson.py diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py index e795023..a429631 100644 --- a/benchmark/benchmark.py +++ b/benchmark/benchmark.py @@ -1,376 +1,15 @@ -# coding=UTF-8 -# Taken from https://raw.githubusercontent.com/esnme/ultrajson/master/tests/benchmark.py - -from __future__ import division, print_function, unicode_literals -import json -import os -import platform -import random -import sys -import timeit - import hyperjson +import ujson +import sys +import random +import pytest +import simplejson +import yajl -if sys.platform == 'win32': - from colorama import init - init() - -USER = {"userId": 3381293, "age": 213, "username": "johndoe", "fullname": "John Doe the Second", - "isAuthorized": True, "liked": 31231.31231202, "approval": 31.1471, "jobs": [1, 2], "currJob": None} -FRIENDS = [USER, USER, USER, USER, USER, USER, USER, USER] - -decode_data = None -test_object = None -skip_lib_comparisons = False -if not skip_lib_comparisons: - import ujson - import simplejson - import yajl - -benchmark_results = [] - - -# ============================================================================= -# Logging benchmarking results. -# ============================================================================= -def results_new_benchmark(name): - benchmark_results.append((name, {}, {})) - print(name) - - -def results_record_result(callback, is_encode, count): - callback_name = callback.__name__ - library = callback_name.split("_")[-1] - try: - results = timeit.repeat("{}()".format(callback_name), "from __main__ import {}".format( - callback_name), repeat=10, number=count) - result = count / min(results) - except TypeError as e: - print(e) - result = 0.0 - benchmark_results[-1][1 if is_encode else 2][library] = result - - print("{} {}: {:.02f} calls/sec".format(library, - "encode" if is_encode else "decode", result)) - - -def results_output_table(): - LIBRARIES = ("hyperjson", "ujson", "yajl", "simplejson", "json") - ENDC = '\033[0m' - GREEN = '\033[92m' - - uname_system, _, uname_release, uname_version, _, uname_processor = platform.uname() - print() - print("~~~~~~~~~~~~~") - print("Test machine:") - print("~~~~~~~~~~~~~") - print() - print(uname_system, uname_release, uname_processor, uname_version) - print() - - column_widths = [max(len(r[0]) for r in benchmark_results)] - for library in LIBRARIES: - column_widths.append(max(10, len(library))) - - line = "+{}+".format("+".join("-"*(width + 2) for width in column_widths)) - columns = [" "*(width + 2) for width in column_widths] - for i, library in enumerate(LIBRARIES): - columns[i + 1] = (" " + library).ljust(column_widths[i + 1] + 2) - print(line) - print("|{}|".format("|".join(columns))) - print(line.replace("-", "=")) - - for name, encodes, decodes in benchmark_results: - columns = [" " * (width + 2) for width in column_widths] - columns[0] = (" " + name).ljust(column_widths[0] + 2) - print("|{}|".format("|".join(columns))) - print(line) - - columns = [None] * len(column_widths) - columns[0] = " encode".ljust(column_widths[0] + 2) - best = max([encodes[library] for library in LIBRARIES]) - for i, library in enumerate(LIBRARIES): - if library in encodes: - if encodes[library] == best: - s = GREEN - else: - s = '' - columns[i + 1] = s + "{:.2f} ".format( - encodes[library]).rjust(column_widths[i + 1] + 2) + ENDC - else: - columns[i + 1] = " "*(column_widths[i + 1] + 2) - print("|{}|".format("|".join(columns))) - print(line) - - if decodes: - columns = [None] * len(column_widths) - columns[0] = " decode".ljust(column_widths[0] + 2) - best = max([decodes[library] for library in LIBRARIES]) - for i, library in enumerate(LIBRARIES): - if library in decodes: - if decodes[library] == best: - s = GREEN - else: - s = '' - columns[i + 1] = s + "{:.2f} ".format( - decodes[library]).rjust(column_widths[i + 1] + 2) + ENDC - else: - columns[i + 1] = " "*(column_widths[i + 1] + 2) - print("|{}|".format("|".join(columns))) - print(line) - - -# ============================================================================= -# JSON encoding. -# ============================================================================= -def dumps_with_hyperjson(): - hyperjson.dumps(test_object) - - -def dumps_with_json(): - json.dumps(test_object) - - -def dumps_with_simplejson(): - simplejson.dumps(test_object) - - -def dumps_with_ujson(): - ujson.dumps(test_object, ensure_ascii=False) - - -def dumps_with_yajl(): - yajl.dumps(test_object) - - -# ============================================================================= -# JSON encoding with sort_keys=True. -# ============================================================================= -def dumps_sorted_with_json(): - json.dumps(test_object, sort_keys=True) - - -def dumps_sorted_with_yajl(): - yajl.dumps(test_object, sort_keys=True) - - -def dumps_sorted_with_hyperjson(): - hyperjson.dumps(test_object, sort_keys=True) - - -def dumps_sorted_with_simplejson(): - simplejson.dumps(test_object, sort_keys=True) - - -def dumps_sorted_with_ujson(): - ujson.dumps(test_object, ensure_ascii=False, sort_keys=True) - - -# ============================================================================= -# JSON decoding. -# ============================================================================= -def loads_with_hyperjson(): - hyperjson.loads(decode_data) - - -def loads_with_json(): - json.loads(decode_data) - - -def loads_with_simplejson(): - simplejson.loads(decode_data) - - -def loads_with_ujson(): - ujson.loads(decode_data) - - -def loads_with_yajl(): - yajl.loads(decode_data) - - -# ============================================================================= -# Benchmarks. -# ============================================================================= -def run_decode(count): - results_record_result(loads_with_hyperjson, False, count) - if not skip_lib_comparisons: - results_record_result(loads_with_ujson, False, count) - results_record_result(loads_with_simplejson, False, count) - results_record_result(loads_with_yajl, False, count) - results_record_result(loads_with_json, False, count) - - -def run_encode(count): - results_record_result(dumps_with_hyperjson, True, count) - if not skip_lib_comparisons: - results_record_result(dumps_with_ujson, True, count) - results_record_result(dumps_with_simplejson, True, count) - results_record_result(dumps_with_yajl, True, count) - results_record_result(dumps_with_json, True, count) - - -def run_encode_sort_keys(count): - results_record_result(dumps_sorted_with_hyperjson, True, count) - if not skip_lib_comparisons: - results_record_result(dumps_sorted_with_ujson, True, count) - results_record_result(dumps_sorted_with_simplejson, True, count) - results_record_result(dumps_sorted_with_yajl, True, count) - results_record_result(dumps_sorted_with_json, True, count) - +LIBRARIES = [hyperjson, ujson, simplejson, yajl] -def benchmark_array_doubles(): - global decode_data, test_object - results_new_benchmark("Array with 256 doubles") - COUNT = 10000 +@pytest.mark.parametrize('lib', LIBRARIES, ids=lambda l: l.__name__) +def test_array_doubles(lib, benchmark): test_object = [sys.maxsize * random.random() for _ in range(256)] - - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -def benchmark_array_utf8_strings(): - global decode_data, test_object - results_new_benchmark("Array with 256 UTF-8 strings") - COUNT = 2000 - - s = "نظام الحكم سلطاني وراثي في الذكور من ذرية السيد تركي بن سعيد بن سلطان ويشترط فيمن يختار لولاية الحكم من بينهم ان يكون مسلما رشيدا عاقلا ًوابنا شرعيا لابوين عمانيين " - test_object = [s] * 256 - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -def benchmark_array_byte_strings(): - global decode_data, test_object - results_new_benchmark("Array with 256 strings") - COUNT = 10000 - - test_object = ["A pretty long string which is in a list"] * 256 - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -def benchmark_medium_complex_object(): - global decode_data, test_object - results_new_benchmark("Medium complex object") - COUNT = 5000 - - test_object = [[USER, FRIENDS], [USER, FRIENDS], [USER, FRIENDS], [ - USER, FRIENDS], [USER, FRIENDS], [USER, FRIENDS]] - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -def benchmark_array_true_values(): - global decode_data, test_object - results_new_benchmark("Array with 256 True values") - COUNT = 50000 - - test_object = [True] * 256 - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -def benchmark_array_of_dict_string_int_pairs(): - global decode_data, test_object - results_new_benchmark("Array with 256 dict{string, int} pairs") - COUNT = 5000 - - test_object = [] - for x in range(256): - test_object.append( - {str(random.random()*20): int(random.random()*1000000)}) - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -def benchmark_dict_of_arrays_of_dict_string_int_pairs(): - global decode_data, test_object - results_new_benchmark( - "Dict with 256 arrays with 256 dict{string, int} pairs") - COUNT = 50 - - test_object = {} - for _ in range(256): - arrays = [{str(random.random()*20): int(random.random()*1000000)} - for _ in range(256)] - test_object[str(random.random()*20)] = arrays - run_encode(COUNT) - - decode_data = json.dumps(test_object) - run_decode(COUNT) - - decode_data = None - - results_new_benchmark( - "Dict with 256 arrays with 256 dict{string, int} pairs, outputting sorted keys") - run_encode_sort_keys(COUNT) - - test_object = None - - -def benchmark_complex_object(): - global decode_data, test_object - results_new_benchmark("Complex object") - COUNT = 100 - - with open(os.path.join(os.path.dirname(__file__), "sample.json"), "r") as f: - test_object = json.load(f) - run_encode(COUNT) - - decode_data = json.dumps(test_object) - test_object = None - run_decode(COUNT) - - decode_data = None - - -# ============================================================================= -# Main. -# ============================================================================= -if __name__ == "__main__": - if len(sys.argv) > 1 and "skip-lib-comps" in sys.argv: - skip_lib_comparisons = True - - benchmark_array_doubles() - benchmark_array_utf8_strings() - benchmark_array_byte_strings() - benchmark_medium_complex_object() - benchmark_array_true_values() - benchmark_array_of_dict_string_int_pairs() - benchmark_dict_of_arrays_of_dict_string_int_pairs() - # Disabled for now because of https://github.com/PyO3/pyo3/issues/177 - # benchmark_complex_object() - if not skip_lib_comparisons: - results_output_table() + benchmark(lib.dumps, test_object) diff --git a/benchmark/benchmark_ujson.py b/benchmark/benchmark_ujson.py new file mode 100644 index 0000000..e795023 --- /dev/null +++ b/benchmark/benchmark_ujson.py @@ -0,0 +1,376 @@ +# coding=UTF-8 +# Taken from https://raw.githubusercontent.com/esnme/ultrajson/master/tests/benchmark.py + +from __future__ import division, print_function, unicode_literals +import json +import os +import platform +import random +import sys +import timeit + +import hyperjson + +if sys.platform == 'win32': + from colorama import init + init() + +USER = {"userId": 3381293, "age": 213, "username": "johndoe", "fullname": "John Doe the Second", + "isAuthorized": True, "liked": 31231.31231202, "approval": 31.1471, "jobs": [1, 2], "currJob": None} +FRIENDS = [USER, USER, USER, USER, USER, USER, USER, USER] + +decode_data = None +test_object = None +skip_lib_comparisons = False +if not skip_lib_comparisons: + import ujson + import simplejson + import yajl + +benchmark_results = [] + + +# ============================================================================= +# Logging benchmarking results. +# ============================================================================= +def results_new_benchmark(name): + benchmark_results.append((name, {}, {})) + print(name) + + +def results_record_result(callback, is_encode, count): + callback_name = callback.__name__ + library = callback_name.split("_")[-1] + try: + results = timeit.repeat("{}()".format(callback_name), "from __main__ import {}".format( + callback_name), repeat=10, number=count) + result = count / min(results) + except TypeError as e: + print(e) + result = 0.0 + benchmark_results[-1][1 if is_encode else 2][library] = result + + print("{} {}: {:.02f} calls/sec".format(library, + "encode" if is_encode else "decode", result)) + + +def results_output_table(): + LIBRARIES = ("hyperjson", "ujson", "yajl", "simplejson", "json") + ENDC = '\033[0m' + GREEN = '\033[92m' + + uname_system, _, uname_release, uname_version, _, uname_processor = platform.uname() + print() + print("~~~~~~~~~~~~~") + print("Test machine:") + print("~~~~~~~~~~~~~") + print() + print(uname_system, uname_release, uname_processor, uname_version) + print() + + column_widths = [max(len(r[0]) for r in benchmark_results)] + for library in LIBRARIES: + column_widths.append(max(10, len(library))) + + line = "+{}+".format("+".join("-"*(width + 2) for width in column_widths)) + columns = [" "*(width + 2) for width in column_widths] + for i, library in enumerate(LIBRARIES): + columns[i + 1] = (" " + library).ljust(column_widths[i + 1] + 2) + print(line) + print("|{}|".format("|".join(columns))) + print(line.replace("-", "=")) + + for name, encodes, decodes in benchmark_results: + columns = [" " * (width + 2) for width in column_widths] + columns[0] = (" " + name).ljust(column_widths[0] + 2) + print("|{}|".format("|".join(columns))) + print(line) + + columns = [None] * len(column_widths) + columns[0] = " encode".ljust(column_widths[0] + 2) + best = max([encodes[library] for library in LIBRARIES]) + for i, library in enumerate(LIBRARIES): + if library in encodes: + if encodes[library] == best: + s = GREEN + else: + s = '' + columns[i + 1] = s + "{:.2f} ".format( + encodes[library]).rjust(column_widths[i + 1] + 2) + ENDC + else: + columns[i + 1] = " "*(column_widths[i + 1] + 2) + print("|{}|".format("|".join(columns))) + print(line) + + if decodes: + columns = [None] * len(column_widths) + columns[0] = " decode".ljust(column_widths[0] + 2) + best = max([decodes[library] for library in LIBRARIES]) + for i, library in enumerate(LIBRARIES): + if library in decodes: + if decodes[library] == best: + s = GREEN + else: + s = '' + columns[i + 1] = s + "{:.2f} ".format( + decodes[library]).rjust(column_widths[i + 1] + 2) + ENDC + else: + columns[i + 1] = " "*(column_widths[i + 1] + 2) + print("|{}|".format("|".join(columns))) + print(line) + + +# ============================================================================= +# JSON encoding. +# ============================================================================= +def dumps_with_hyperjson(): + hyperjson.dumps(test_object) + + +def dumps_with_json(): + json.dumps(test_object) + + +def dumps_with_simplejson(): + simplejson.dumps(test_object) + + +def dumps_with_ujson(): + ujson.dumps(test_object, ensure_ascii=False) + + +def dumps_with_yajl(): + yajl.dumps(test_object) + + +# ============================================================================= +# JSON encoding with sort_keys=True. +# ============================================================================= +def dumps_sorted_with_json(): + json.dumps(test_object, sort_keys=True) + + +def dumps_sorted_with_yajl(): + yajl.dumps(test_object, sort_keys=True) + + +def dumps_sorted_with_hyperjson(): + hyperjson.dumps(test_object, sort_keys=True) + + +def dumps_sorted_with_simplejson(): + simplejson.dumps(test_object, sort_keys=True) + + +def dumps_sorted_with_ujson(): + ujson.dumps(test_object, ensure_ascii=False, sort_keys=True) + + +# ============================================================================= +# JSON decoding. +# ============================================================================= +def loads_with_hyperjson(): + hyperjson.loads(decode_data) + + +def loads_with_json(): + json.loads(decode_data) + + +def loads_with_simplejson(): + simplejson.loads(decode_data) + + +def loads_with_ujson(): + ujson.loads(decode_data) + + +def loads_with_yajl(): + yajl.loads(decode_data) + + +# ============================================================================= +# Benchmarks. +# ============================================================================= +def run_decode(count): + results_record_result(loads_with_hyperjson, False, count) + if not skip_lib_comparisons: + results_record_result(loads_with_ujson, False, count) + results_record_result(loads_with_simplejson, False, count) + results_record_result(loads_with_yajl, False, count) + results_record_result(loads_with_json, False, count) + + +def run_encode(count): + results_record_result(dumps_with_hyperjson, True, count) + if not skip_lib_comparisons: + results_record_result(dumps_with_ujson, True, count) + results_record_result(dumps_with_simplejson, True, count) + results_record_result(dumps_with_yajl, True, count) + results_record_result(dumps_with_json, True, count) + + +def run_encode_sort_keys(count): + results_record_result(dumps_sorted_with_hyperjson, True, count) + if not skip_lib_comparisons: + results_record_result(dumps_sorted_with_ujson, True, count) + results_record_result(dumps_sorted_with_simplejson, True, count) + results_record_result(dumps_sorted_with_yajl, True, count) + results_record_result(dumps_sorted_with_json, True, count) + + +def benchmark_array_doubles(): + global decode_data, test_object + results_new_benchmark("Array with 256 doubles") + COUNT = 10000 + + test_object = [sys.maxsize * random.random() for _ in range(256)] + + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +def benchmark_array_utf8_strings(): + global decode_data, test_object + results_new_benchmark("Array with 256 UTF-8 strings") + COUNT = 2000 + + s = "نظام الحكم سلطاني وراثي في الذكور من ذرية السيد تركي بن سعيد بن سلطان ويشترط فيمن يختار لولاية الحكم من بينهم ان يكون مسلما رشيدا عاقلا ًوابنا شرعيا لابوين عمانيين " + test_object = [s] * 256 + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +def benchmark_array_byte_strings(): + global decode_data, test_object + results_new_benchmark("Array with 256 strings") + COUNT = 10000 + + test_object = ["A pretty long string which is in a list"] * 256 + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +def benchmark_medium_complex_object(): + global decode_data, test_object + results_new_benchmark("Medium complex object") + COUNT = 5000 + + test_object = [[USER, FRIENDS], [USER, FRIENDS], [USER, FRIENDS], [ + USER, FRIENDS], [USER, FRIENDS], [USER, FRIENDS]] + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +def benchmark_array_true_values(): + global decode_data, test_object + results_new_benchmark("Array with 256 True values") + COUNT = 50000 + + test_object = [True] * 256 + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +def benchmark_array_of_dict_string_int_pairs(): + global decode_data, test_object + results_new_benchmark("Array with 256 dict{string, int} pairs") + COUNT = 5000 + + test_object = [] + for x in range(256): + test_object.append( + {str(random.random()*20): int(random.random()*1000000)}) + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +def benchmark_dict_of_arrays_of_dict_string_int_pairs(): + global decode_data, test_object + results_new_benchmark( + "Dict with 256 arrays with 256 dict{string, int} pairs") + COUNT = 50 + + test_object = {} + for _ in range(256): + arrays = [{str(random.random()*20): int(random.random()*1000000)} + for _ in range(256)] + test_object[str(random.random()*20)] = arrays + run_encode(COUNT) + + decode_data = json.dumps(test_object) + run_decode(COUNT) + + decode_data = None + + results_new_benchmark( + "Dict with 256 arrays with 256 dict{string, int} pairs, outputting sorted keys") + run_encode_sort_keys(COUNT) + + test_object = None + + +def benchmark_complex_object(): + global decode_data, test_object + results_new_benchmark("Complex object") + COUNT = 100 + + with open(os.path.join(os.path.dirname(__file__), "sample.json"), "r") as f: + test_object = json.load(f) + run_encode(COUNT) + + decode_data = json.dumps(test_object) + test_object = None + run_decode(COUNT) + + decode_data = None + + +# ============================================================================= +# Main. +# ============================================================================= +if __name__ == "__main__": + if len(sys.argv) > 1 and "skip-lib-comps" in sys.argv: + skip_lib_comparisons = True + + benchmark_array_doubles() + benchmark_array_utf8_strings() + benchmark_array_byte_strings() + benchmark_medium_complex_object() + benchmark_array_true_values() + benchmark_array_of_dict_string_int_pairs() + benchmark_dict_of_arrays_of_dict_string_int_pairs() + # Disabled for now because of https://github.com/PyO3/pyo3/issues/177 + # benchmark_complex_object() + if not skip_lib_comparisons: + results_output_table() From 83fef6eb9fed7258bcbd1727d8fbacf1283a05b5 Mon Sep 17 00:00:00 2001 From: ramin Date: Thu, 5 Jul 2018 23:51:45 +0200 Subject: [PATCH 2/9] pytest benchmarks --- benchmark/benchmark.py | 15 ---- {benchmark => benchmarks}/benchmark_ujson.py | 0 benchmarks/conftest.py | 61 ++++++++++++++ .../dict_string_int_plain.txt | 0 {benchmark => benchmarks}/sample.json | 0 benchmarks/test_benchmark.py | 80 +++++++++++++++++++ 6 files changed, 141 insertions(+), 15 deletions(-) delete mode 100644 benchmark/benchmark.py rename {benchmark => benchmarks}/benchmark_ujson.py (100%) create mode 100644 benchmarks/conftest.py rename {benchmark => benchmarks}/dict_string_int_plain.txt (100%) rename {benchmark => benchmarks}/sample.json (100%) create mode 100644 benchmarks/test_benchmark.py diff --git a/benchmark/benchmark.py b/benchmark/benchmark.py deleted file mode 100644 index a429631..0000000 --- a/benchmark/benchmark.py +++ /dev/null @@ -1,15 +0,0 @@ -import hyperjson -import ujson -import sys -import random -import pytest -import simplejson -import yajl - -LIBRARIES = [hyperjson, ujson, simplejson, yajl] - - -@pytest.mark.parametrize('lib', LIBRARIES, ids=lambda l: l.__name__) -def test_array_doubles(lib, benchmark): - test_object = [sys.maxsize * random.random() for _ in range(256)] - benchmark(lib.dumps, test_object) diff --git a/benchmark/benchmark_ujson.py b/benchmarks/benchmark_ujson.py similarity index 100% rename from benchmark/benchmark_ujson.py rename to benchmarks/benchmark_ujson.py diff --git a/benchmarks/conftest.py b/benchmarks/conftest.py new file mode 100644 index 0000000..06aac5f --- /dev/null +++ b/benchmarks/conftest.py @@ -0,0 +1,61 @@ +# based on https://github.com/lelit/python-rapidjson/blob/master/benchmarks/conftest.py +import hyperjson +from collections import namedtuple +from operator import attrgetter + +Contender = namedtuple('Contender', 'name,dumps,loads') + + +def pytest_benchmark_group_stats(config, benchmarks, group_by): + result = {} + for bench in benchmarks: + engine, data_kind = bench['param'].split('-') + group = result.setdefault("%s: %s" % (data_kind, bench['group']), []) + group.append(bench) + return sorted(result.items()) + + +def pytest_addoption(parser): + parser.addoption('--compare-other-engines', action='store_true', + help='compare against other JSON engines') + + +contenders = [] + +contenders.append(Contender('hyperjson', + hyperjson.dumps, + hyperjson.loads)) +try: + import simplejson +except ImportError: + pass +else: + contenders.append(Contender('simplejson', + simplejson.dumps, + simplejson.loads)) +try: + import ujson +except ImportError: + pass +else: + contenders.append(Contender('ujson', + ujson.dumps, + ujson.loads)) +try: + import yajl +except ImportError: + pass +else: + contenders.append(Contender('yajl', + yajl.dumps, + yajl.loads)) + + +def pytest_generate_tests(metafunc): + if 'contender' in metafunc.fixturenames: + if metafunc.config.option.compare_other_engines: + metafunc.parametrize('contender', contenders, + ids=attrgetter('name')) + else: + metafunc.parametrize( + 'contender', contenders[:1], ids=attrgetter('name')) diff --git a/benchmark/dict_string_int_plain.txt b/benchmarks/dict_string_int_plain.txt similarity index 100% rename from benchmark/dict_string_int_plain.txt rename to benchmarks/dict_string_int_plain.txt diff --git a/benchmark/sample.json b/benchmarks/sample.json similarity index 100% rename from benchmark/sample.json rename to benchmarks/sample.json diff --git a/benchmarks/test_benchmark.py b/benchmarks/test_benchmark.py new file mode 100644 index 0000000..5228888 --- /dev/null +++ b/benchmarks/test_benchmark.py @@ -0,0 +1,80 @@ +import sys +import random +import pytest + +benchmark = pytest.importorskip('pytest_benchmark') + +doubles = [] +numbers = [] +unicode_strings = [] +strings = [] +booleans = [] +list_dicts = [] +dict_lists = {} + +composite_object = { + 'words': """ + Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Mauris adipiscing adipiscing placerat. + Vestibulum augue augue, + pellentesque quis sollicitudin id, adipiscing. + """, + 'list': list(range(200)), + 'dict': dict((str(i), 'a') for i in list(range(200))), + 'int': 100100100, + 'float': 100999.123456 +} +for x in range(256): + doubles.append(sys.maxsize * random.random()) + unicode_strings.append( + "نظام الحكم سلطاني وراثي في الذكور من ذرية السيد تركي بن سعيد بن سلطان ويشترط فيمن يختار لولاية الحكم من بينهم ان يكون مسلما رشيدا عاقلا ًوابنا شرعيا لابوين عمانيين ") + strings.append("A pretty long string which is in a list") + booleans.append(True) + +for y in range(100): + arrays = [] + list_dicts.append({str(random.random()*20): int(random.random()*1000000)}) + + for x in range(100): + arrays.append({str(random.random() * 20): int(random.random()*1000000)}) + dict_lists[str(random.random() * 20)] = arrays + +user = { + "userId": 3381293, + "age": 213, + "username": "johndoe", + "fullname": u"John Doe the Second", + "isAuthorized": True, + "liked": 31231.31231202, + "approval": 31.1471, + "jobs": [1, 2], + "currJob": None +} + +friends = [user, user, user, user, user, user, user, user] +complex_object = [ + [user, friends], [user, friends], [user, friends], + [user, friends], [user, friends], [user, friends] +] +datasets = [('composite object', composite_object), + ('256 doubles array', doubles), + ('256 unicode array', unicode_strings), + ('256 ascii array', strings), + ('256 Trues array', booleans), + ('100 dicts array', list_dicts), + # ('100 arrays dict', dict_lists), + ('complex object', complex_object), + ] + + +@pytest.mark.benchmark(group='serialize') +@pytest.mark.parametrize('data', [d[1] for d in datasets], ids=[d[0] for d in datasets]) +def test_dumps(contender, data, benchmark): + benchmark(contender.dumps, data) + + +@pytest.mark.benchmark(group='deserialize') +@pytest.mark.parametrize('data', [d[1] for d in datasets], ids=[d[0] for d in datasets]) +def test_loads(contender, data, benchmark): + data = contender.dumps(data) + benchmark(contender.loads, data) From efdbe956627b17adfe4f93f8c80b07161ed28dbe Mon Sep 17 00:00:00 2001 From: ramin Date: Fri, 6 Jul 2018 20:33:12 +0200 Subject: [PATCH 3/9] pytest benchmark --- Makefile | 4 ++-- benchmarks/test_benchmark.py | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 141eaa8..65e7fe0 100644 --- a/Makefile +++ b/Makefile @@ -24,8 +24,8 @@ test: .PHONY: bench bench: - python3 benchmark/benchmark.py skip-lib-comps + pytest benchmarks .PHONY: bench-all bench-all: - python3 benchmark/benchmark.py \ No newline at end of file + pytest benchmarks --compare-other-engines \ No newline at end of file diff --git a/benchmarks/test_benchmark.py b/benchmarks/test_benchmark.py index 5228888..216f31d 100644 --- a/benchmarks/test_benchmark.py +++ b/benchmarks/test_benchmark.py @@ -24,12 +24,13 @@ 'int': 100100100, 'float': 100999.123456 } -for x in range(256): - doubles.append(sys.maxsize * random.random()) - unicode_strings.append( - "نظام الحكم سلطاني وراثي في الذكور من ذرية السيد تركي بن سعيد بن سلطان ويشترط فيمن يختار لولاية الحكم من بينهم ان يكون مسلما رشيدا عاقلا ًوابنا شرعيا لابوين عمانيين ") - strings.append("A pretty long string which is in a list") - booleans.append(True) + +doubles = [sys.maxsize * random.random() for _ in range(256)] +unicode_strings = [ + "نظام الحكم سلطاني وراثي في الذكور من ذرية السيد تركي بن سعيد بن سلطان ويشترط فيمن يختار لولاية الحكم من بينهم ان يكون مسلما رشيدا عاقلا ًوابنا شرعيا لابوين عمانيين " for _ in range(256)] +strings = ["A pretty long string which is in a list"] * 256 + +booleans = [True] * 256 for y in range(100): arrays = [] From 4fc3e4343df62b5671d869e1a2cd45e1776de41d Mon Sep 17 00:00:00 2001 From: ramin Date: Fri, 6 Jul 2018 20:39:38 +0200 Subject: [PATCH 4/9] pytest benchmark --- Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 65e7fe0..460ce89 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,7 @@ install: nightly .PHONY: test test: - # Run tests outside of project folder. - # See https://github.com/PyO3/pyo3/issues/105 - cd .. && pytest --verbose --capture=no $(DIR) + pytest tests .PHONY: bench bench: From f57d3ee683e2b43dee50e53c6fef1e5585d49dd0 Mon Sep 17 00:00:00 2001 From: ramin Date: Fri, 6 Jul 2018 20:46:42 +0200 Subject: [PATCH 5/9] pytest benchmark --- requirements-test.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements-test.txt b/requirements-test.txt index c17d467..3d3764e 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,4 @@ pytest>=3.5.0 pytest-runner>=4.2 # add setup.py test support for pytest +pytest-benchmark>=3.1 + From 35abd66e237a1618af515757ec4d5a5d608587ba Mon Sep 17 00:00:00 2001 From: ramin Date: Fri, 6 Jul 2018 22:02:21 +0200 Subject: [PATCH 6/9] pytest benchmark --- benchmarks/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/conftest.py b/benchmarks/conftest.py index 06aac5f..6e55f61 100644 --- a/benchmarks/conftest.py +++ b/benchmarks/conftest.py @@ -53,7 +53,7 @@ def pytest_addoption(parser): def pytest_generate_tests(metafunc): if 'contender' in metafunc.fixturenames: - if metafunc.config.option.compare_other_engines: + if metafunc.config.getoption('compare_other_engines'): metafunc.parametrize('contender', contenders, ids=attrgetter('name')) else: From f6abdc34c22f65df1642357c7b90325d30fc37b0 Mon Sep 17 00:00:00 2001 From: ramin Date: Fri, 6 Jul 2018 23:34:11 +0200 Subject: [PATCH 7/9] pytest benchmark --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 460ce89..5309974 100644 --- a/Makefile +++ b/Makefile @@ -26,4 +26,4 @@ bench: .PHONY: bench-all bench-all: - pytest benchmarks --compare-other-engines \ No newline at end of file + pytest benchmarks --help \ No newline at end of file From c8e20a2e961dc58633795181a2ea32b23058cf42 Mon Sep 17 00:00:00 2001 From: ramin Date: Fri, 6 Jul 2018 23:49:16 +0200 Subject: [PATCH 8/9] pytest benchmark --- Makefile | 2 +- benchmarks/conftest.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 5309974..53ebc79 100644 --- a/Makefile +++ b/Makefile @@ -26,4 +26,4 @@ bench: .PHONY: bench-all bench-all: - pytest benchmarks --help \ No newline at end of file + pytest benchmarks --compare \ No newline at end of file diff --git a/benchmarks/conftest.py b/benchmarks/conftest.py index 6e55f61..d1e5a07 100644 --- a/benchmarks/conftest.py +++ b/benchmarks/conftest.py @@ -16,7 +16,7 @@ def pytest_benchmark_group_stats(config, benchmarks, group_by): def pytest_addoption(parser): - parser.addoption('--compare-other-engines', action='store_true', + parser.addoption('--compare', action='store_true', help='compare against other JSON engines') @@ -53,7 +53,7 @@ def pytest_addoption(parser): def pytest_generate_tests(metafunc): if 'contender' in metafunc.fixturenames: - if metafunc.config.getoption('compare_other_engines'): + if metafunc.config.getoption('compare'): metafunc.parametrize('contender', contenders, ids=attrgetter('name')) else: From 146f52daa3a29027e0eab52d920114c06e306658 Mon Sep 17 00:00:00 2001 From: ramin Date: Sat, 7 Jul 2018 14:25:14 +0200 Subject: [PATCH 9/9] pytest benchmark --- benchmarks/conftest.py => conftest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename benchmarks/conftest.py => conftest.py (100%) diff --git a/benchmarks/conftest.py b/conftest.py similarity index 100% rename from benchmarks/conftest.py rename to conftest.py