From 2895a7125e421ecdcb97142850b432de1e429569 Mon Sep 17 00:00:00 2001 From: Lele Gaifax Date: Thu, 24 Mar 2016 18:44:07 +0100 Subject: [PATCH 1/3] Use pytest-benchmark plugin This greatly simplifies the benchmark test module, producing a much more detailed report. The plugin is also able to compare successive runs, see http://pytest-benchmark.readthedocs.org/en/stable/comparing.html. --- requirements-test.txt | 1 + tests/conftest.py | 8 + tests/test_benchmark.py | 353 ++++++++++------------------------------ 3 files changed, 91 insertions(+), 271 deletions(-) create mode 100644 tests/conftest.py diff --git a/requirements-test.txt b/requirements-test.txt index f9b2918..bca568b 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,4 +1,5 @@ pytest +pytest-benchmark pytz simplejson ujson diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..d1ffc97 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +# See https://github.com/ionelmc/pytest-benchmark/issues/48 + +def pytest_benchmark_group_stats(config, benchmarks, group_by): + result = {} + for bench in benchmarks: + data_kind = bench.param.split('-')[0] + result.setdefault("%s: %s" % (data_kind, bench.group), []).append(bench) + return sorted(result.items()) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 47ba9a8..6f0bd12 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -1,6 +1,5 @@ import datetime import pytest -import time import sys import random from functools import partial @@ -30,6 +29,38 @@ except ImportError: ujson = None +contenders = [] +unprecise_contenders = [] + +if yajl: + contenders.append(('yajl', yajl.Encoder().encode, yajl.Decoder().decode)) + +if simplejson: + contenders.append(('simplejson', simplejson.dumps, simplejson.loads)) + +if json: + contenders.append(('stdlib json', json.dumps, json.loads)) + +if rapidjson: + contenders.append( + ('rapidjson', rapidjson.dumps, + partial(rapidjson.loads, precise_float=True)) + ) + unprecise_contenders.append( + ('(rapidjson not precise)', rapidjson.dumps, + partial(rapidjson.loads, precise_float=False)) + ) + +if ujson: + contenders.append( + ('ujson', ujson.dumps, partial(ujson.loads, precise_float=True)) + ) + unprecise_contenders.append( + ('(ujson not precise)', ujson.dumps, + partial(ujson.loads, precise_float=False)) + ) + + default_data = { 'words': """ Lorem ipsum dolor sit amet, consectetur adipiscing @@ -57,74 +88,11 @@ friends = [ user, user, user, user, user, user, user, user ] - -def time_func(func, data, iterations): - start = time.time() - while iterations: - iterations -= 1 - func(data) - return time.time() - start - - -def run_client_test( - name, serialize, deserialize, iterations=100*1000, data=default_data -): - squashed_data = serialize(data) - serialize_profile = time_func(serialize, data, iterations) - deserialize_profile = time_func(deserialize, squashed_data, iterations) - return serialize_profile, deserialize_profile - -contenders = [] - -if yajl: - contenders.append(('yajl', yajl.Encoder().encode, yajl.Decoder().decode)) - -if simplejson: - contenders.append(('simplejson', simplejson.dumps, simplejson.loads)) - -if json: - contenders.append(('stdlib json', json.dumps, json.loads)) - -if rapidjson: - contenders.append( - ('rapidjson', rapidjson.dumps, - partial(rapidjson.loads, precise_float=True)) - ) - -if ujson: - contenders.append( - ('ujson', ujson.dumps, partial(ujson.loads, precise_float=True)) - ) - -unprecise_rapid = ( - 'rapidjson (not precise)', - rapidjson.dumps, - partial(rapidjson.loads, precise_float=False) -) - -unprecise_ujson = ( - 'ujson (not precise)', - ujson.dumps, - partial(ujson.loads, precise_float=False) -) - -datetime_none_rapid = ( - 'rapidjson (dtm=None)', - partial(rapidjson.dumps, datetime_mode=None), - partial(rapidjson.loads, datetime_mode=None) -) - -datetime_iso8601_rapid = ( - 'rapidjson (dtm=ISO8601)', - partial(rapidjson.dumps, datetime_mode=rapidjson.DATETIME_MODE_ISO8601), - partial(rapidjson.loads, datetime_mode=rapidjson.DATETIME_MODE_ISO8601) -) - - doubles = [] unicode_strings = [] strings = [] booleans = [] +datetimes = [] list_dicts = [] dict_lists = {} @@ -138,6 +106,13 @@ def run_client_test( unicode_strings.append("نظام الحكم سلطاني وراثي في الذكور من ذرية السيد تركي بن سعيد بن سلطان ويشترط فيمن يختار لولاية الحكم من بينهم ان يكون مسلما رشيدا عاقلا ًوابنا شرعيا لابوين عمانيين ") strings.append("A pretty long string which is in a list") booleans.append(True) + datetimes.append([datetime.date(random.randint(1900, 2000), + random.randint(1, 12), + random.randint(1, 28)), + datetime.time(random.randint(0,23), + random.randint(0, 59), + random.randint(0, 59)), + datetime.datetime.now()]) for y in range(100): arrays = [] @@ -147,219 +122,55 @@ def run_client_test( arrays.append({str(random.random() * 20): int(random.random()*1000000)}) dict_lists[str(random.random() * 20)] = arrays +datasets = [('composite object', default_data), + ('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), + ('medium complex objects', medium_complex), +] -@pytest.mark.benchmark -@pytest.mark.parametrize('name,serialize,deserialize', contenders) -def test_json_serialization(name, serialize, deserialize): - ser_data, des_data = run_client_test(name, serialize, deserialize) - msg = "\n%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize( - 'name,serialize,deserialize', - contenders + [unprecise_ujson, unprecise_rapid] -) -def test_json_doubles(name, serialize, deserialize): - print("\nArray with 256 doubles:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=doubles, - iterations=50000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize('name,serialize,deserialize', contenders) -def test_json_unicode_strings(name, serialize, deserialize): - print("\nArray with 256 unicode strings:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=unicode_strings, - iterations=5000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize('name,serialize,deserialize', contenders) -def test_json_scii_strings(name, serialize, deserialize): - print("\nArray with 256 ascii strings:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=strings, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize('name,serialize,deserialize', contenders) -def test_json_booleans(name, serialize, deserialize): - print("\nArray with 256 True's:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=booleans, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize('name,serialize,deserialize', contenders) -def test_json_list_of_dictionaries(name, serialize, deserialize): - print("\nArray of 100 dictionaries:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=list_dicts, - iterations=5, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize('name,serialize,deserialize', contenders) -def test_json_dictionary_of_lists(name, serialize, deserialize): - print("\nDictionary of 100 Arrays:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=dict_lists, - iterations=5, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -@pytest.mark.parametrize( - 'name,serialize,deserialize', - contenders + [datetime_none_rapid, datetime_iso8601_rapid]) -def test_json_medium_complex_objects(name, serialize, deserialize): - print("\n256 Medium Complex objects:") - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=medium_complex, - iterations=50000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - -@pytest.mark.benchmark -def test_double_performance_float_precision(): - from functools import partial - - print("\nArray with 256 doubles:") - name = 'rapidjson (precise)' - serialize = rapidjson.dumps - deserialize = rapidjson.loads - - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=doubles, - iterations=50000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - - print(msg) - - name = 'rapidjson (not precise)' - serialize = rapidjson.dumps - deserialize = partial(rapidjson.loads, precise_float=False) - - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=doubles, - iterations=50000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - print(msg) - - -birthdays_native = [] -birthdays_string = [] -for f in friends: - ndata = dict(f) - ndata['birthday'] = datetime.date(random.randint(1900, 2000), - random.randint(1, 12), - random.randint(1, 28)) - ndata['birthtime'] = datetime.time(random.randint(0,23), - random.randint(0, 59), - random.randint(0, 59)) - ndata['timestamp'] = datetime.datetime.now() - birthdays_native.append(ndata) +@pytest.mark.benchmark(group='serialize') +@pytest.mark.parametrize('serializer', + [c[1] for c in contenders], + ids=[c[0] for c in contenders]) +@pytest.mark.parametrize('data', [d[1] for d in datasets], ids=[d[0] for d in datasets]) +def test_dumps(serializer, data, benchmark): + benchmark(serializer, data) - sdata = dict(ndata) - sdata['birthday'] = ndata['birthday'].isoformat() - sdata['birthtime'] = ndata['birthtime'].isoformat() - sdata['timestamp'] = ndata['timestamp'].isoformat() - birthdays_string.append(sdata) +@pytest.mark.benchmark(group='deserialize') +@pytest.mark.parametrize('serializer,deserializer', + [(c[1], c[2]) for c in contenders], + ids=[c[0] for c in contenders]) +@pytest.mark.parametrize('data', [d[1] for d in datasets], ids=[d[0] for d in datasets]) +def test_loads(serializer, deserializer, data, benchmark): + data = serializer(data) + benchmark(deserializer, data) -@pytest.mark.benchmark -def test_datetime_performance(): - from functools import partial - print("\nDatetimes:") - name = 'rapidjson (string)' - serialize = rapidjson.dumps - deserialize = rapidjson.loads +# Special case 1: precise vs unprecise - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=birthdays_string, - iterations=50000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) +@pytest.mark.benchmark(group='deserialize') +@pytest.mark.parametrize('serializer,deserializer', + [(c[1], c[2]) for c in unprecise_contenders], + ids=['256 doubles array ' + c[0] for c in unprecise_contenders]) +def test_loads_float(serializer, deserializer, benchmark): + data = serializer(doubles) + benchmark(deserializer, data) - print(msg) - name = 'rapidjson (native)' - serialize = partial(rapidjson.dumps, datetime_mode=rapidjson.DATETIME_MODE_ISO8601) - deserialize = partial(rapidjson.loads, datetime_mode=rapidjson.DATETIME_MODE_ISO8601) +# Special case 2: load datetimes as plain strings vs datetime.xxx instances - ser_data, des_data = run_client_test( - name, serialize, deserialize, - data=birthdays_native, - iterations=50000, - ) - msg = "%-11s serialize: %0.3f deserialize: %0.3f total: %0.3f" % ( - name, ser_data, des_data, ser_data + des_data - ) - print(msg) +@pytest.mark.benchmark(group='deserialize') +@pytest.mark.parametrize('deserializer', + [rapidjson.loads, + partial(rapidjson.loads, + datetime_mode=rapidjson.DATETIME_MODE_ISO8601)], + ids=['Ignore datetimes', 'Parse datetimes']) +def test_loads_datetimes(deserializer, benchmark): + data = rapidjson.dumps(datetimes, datetime_mode=rapidjson.DATETIME_MODE_ISO8601) + benchmark(deserializer, data) From e81154da21aae805f3bc4a084f11d404f6b1bf88 Mon Sep 17 00:00:00 2001 From: Lele Gaifax Date: Thu, 24 Mar 2016 19:16:54 +0100 Subject: [PATCH 2/3] Better grouping for not precise floats benchmarks --- tests/conftest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index d1ffc97..2d14844 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,5 +4,9 @@ def pytest_benchmark_group_stats(config, benchmarks, group_by): result = {} for bench in benchmarks: data_kind = bench.param.split('-')[0] - result.setdefault("%s: %s" % (data_kind, bench.group), []).append(bench) + if 'not precise)' in data_kind: + group = result.setdefault("not precise floats: %s" % bench.group, []) + else: + group = result.setdefault("%s: %s" % (data_kind, bench.group), []) + group.append(bench) return sorted(result.items()) From 4d8975a270ad9e8969b45dc0c760c68f0ae77069 Mon Sep 17 00:00:00 2001 From: Lele Gaifax Date: Fri, 25 Mar 2016 12:51:26 +0100 Subject: [PATCH 3/3] Better parametrization of the contender engines A new option ``--compare-other-engines`` determines whether the benchmarks are performed on all available engines or on RapidJSON alone. --- .gitignore | 1 + README.rst | 20 +++++++- tests/conftest.py | 103 +++++++++++++++++++++++++++++++++++-- tests/test_benchmark.py | 109 ++++++++-------------------------------- 4 files changed, 140 insertions(+), 93 deletions(-) diff --git a/.gitignore b/.gitignore index ba74660..c833b86 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ htmlcov/ nosetests.xml coverage.xml *,cover +/.benchmarks/ # Translations *.mo diff --git a/README.rst b/README.rst index 9c5263d..161869f 100644 --- a/README.rst +++ b/README.rst @@ -66,8 +66,26 @@ To run these tests yourself, clone the repo and run: .. code-block:: - $ tox -e py34 -- -m benchmark + $ tox -e py34 -- -m benchmark --compare-other-engines +Without the option ``--compare-other-engines`` it will focus only on ``RapidJSON``. +This is particularly handy coupled with the `compare past runs`__ functionality of +``pytest-benchmark``: + +.. code-block:: + + $ tox -e py34 -- -m benchmark --benchmark-autosave + # hack, hack, hack! + $ tox -e py34 -- -m benchmark --benchmark-compare=0001 + + ----------------------- benchmark 'deserialize': 18 tests ------------------------ + Name (time in us) Min… + ---------------------------------------------------------------------------------- + test_loads[rapidjson-256 Trues array] (NOW) 5.2320 (1.0)… + test_loads[rapidjson-256 Trues array] (0001) 5.4180 (1.04)… + … + +__ http://pytest-benchmark.readthedocs.org/en/latest/comparing.html Incompatibility --------------- diff --git a/tests/conftest.py b/tests/conftest.py index 2d14844..3f1f341 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,107 @@ +from collections import namedtuple +from functools import partial +from operator import attrgetter + +Contender = namedtuple('Contender', 'name,dumps,loads') + + # See https://github.com/ionelmc/pytest-benchmark/issues/48 def pytest_benchmark_group_stats(config, benchmarks, group_by): result = {} for bench in benchmarks: - data_kind = bench.param.split('-')[0] - if 'not precise)' in data_kind: - group = result.setdefault("not precise floats: %s" % bench.group, []) + if config.option.compare_other_engines: + engine, data_kind = bench.param.split('-') + if engine.endswith('not precise'): + group = result.setdefault("not precise floats: %s" % bench.group, []) + else: + group = result.setdefault("%s: %s" % (data_kind, bench.group), []) else: - group = result.setdefault("%s: %s" % (data_kind, bench.group), []) + group = result.setdefault(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 = [] +inaccurate_floats_contenders = [] + +import rapidjson + +contenders.append(Contender('rapidjson', + rapidjson.dumps, + partial(rapidjson.loads, precise_float=True))) + +inaccurate_floats_contenders.append(Contender('rapidjson not precise', + rapidjson.dumps, + partial(rapidjson.loads, + precise_float=False))) + +try: + import yajl +except ImportError: + pass +else: + contenders.append(Contender('yajl', + yajl.Encoder().encode, + yajl.Decoder().decode)) + +try: + import simplejson +except ImportError: + pass +else: + contenders.append(Contender('simplejson', + simplejson.dumps, + simplejson.loads)) + +try: + import json +except ImportError: + pass +else: + contenders.append(Contender('stdlib json', + json.dumps, + json.loads)) + +try: + import ujson +except ImportError: + pass +else: + contenders.append(Contender('ujson', + ujson.dumps, + partial(ujson.loads, precise_float=True))) + inaccurate_floats_contenders.append(Contender('ujson not precise', + ujson.dumps, + partial(ujson.loads, + precise_float=False))) + + +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')) + + if 'inaccurate_floats_contender' in metafunc.fixturenames: + if metafunc.config.option.compare_other_engines: + metafunc.parametrize('inaccurate_floats_contender', + inaccurate_floats_contenders, + ids=attrgetter('name')) + else: + metafunc.parametrize('inaccurate_floats_contender', + inaccurate_floats_contenders[:1], + ids=attrgetter('name')) + + if 'datetimes_loads_contender' in metafunc.fixturenames: + metafunc.parametrize('datetimes_loads_contender', + [rapidjson.loads, + partial(rapidjson.loads, + datetime_mode=rapidjson.DATETIME_MODE_ISO8601)], + ids=['Ignore datetimes', 'Parse datetimes']) diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 6f0bd12..99b2731 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -2,66 +2,9 @@ import pytest import sys import random -from functools import partial - -try: - import yajl -except ImportError: - yajl = None - -try: - import simplejson -except ImportError: - simplejson = None - -try: - import json -except ImportError: - json = None - -try: - import rapidjson -except ImportError: - rapidjson = None - -try: - import ujson -except ImportError: - ujson = None - -contenders = [] -unprecise_contenders = [] - -if yajl: - contenders.append(('yajl', yajl.Encoder().encode, yajl.Decoder().decode)) - -if simplejson: - contenders.append(('simplejson', simplejson.dumps, simplejson.loads)) - -if json: - contenders.append(('stdlib json', json.dumps, json.loads)) - -if rapidjson: - contenders.append( - ('rapidjson', rapidjson.dumps, - partial(rapidjson.loads, precise_float=True)) - ) - unprecise_contenders.append( - ('(rapidjson not precise)', rapidjson.dumps, - partial(rapidjson.loads, precise_float=False)) - ) - -if ujson: - contenders.append( - ('ujson', ujson.dumps, partial(ujson.loads, precise_float=True)) - ) - unprecise_contenders.append( - ('(ujson not precise)', ujson.dumps, - partial(ujson.loads, precise_float=False)) - ) - - -default_data = { + + +composite_object = { 'words': """ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris adipiscing adipiscing placerat. @@ -96,7 +39,7 @@ list_dicts = [] dict_lists = {} -medium_complex = [ +complex_object = [ [user, friends], [user, friends], [user, friends], [user, friends], [user, friends], [user, friends] ] @@ -122,55 +65,45 @@ arrays.append({str(random.random() * 20): int(random.random()*1000000)}) dict_lists[str(random.random() * 20)] = arrays -datasets = [('composite object', default_data), + +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), - ('medium complex objects', medium_complex), + ('complex object', complex_object), ] @pytest.mark.benchmark(group='serialize') -@pytest.mark.parametrize('serializer', - [c[1] for c in contenders], - ids=[c[0] for c in contenders]) @pytest.mark.parametrize('data', [d[1] for d in datasets], ids=[d[0] for d in datasets]) -def test_dumps(serializer, data, benchmark): - benchmark(serializer, data) +def test_dumps(contender, data, benchmark): + benchmark(contender.dumps, data) @pytest.mark.benchmark(group='deserialize') -@pytest.mark.parametrize('serializer,deserializer', - [(c[1], c[2]) for c in contenders], - ids=[c[0] for c in contenders]) @pytest.mark.parametrize('data', [d[1] for d in datasets], ids=[d[0] for d in datasets]) -def test_loads(serializer, deserializer, data, benchmark): - data = serializer(data) - benchmark(deserializer, data) +def test_loads(contender, data, benchmark): + data = contender.dumps(data) + benchmark(contender.loads, data) # Special case 1: precise vs unprecise @pytest.mark.benchmark(group='deserialize') -@pytest.mark.parametrize('serializer,deserializer', - [(c[1], c[2]) for c in unprecise_contenders], - ids=['256 doubles array ' + c[0] for c in unprecise_contenders]) -def test_loads_float(serializer, deserializer, benchmark): - data = serializer(doubles) - benchmark(deserializer, data) +@pytest.mark.parametrize('data', [doubles], ids=['256 doubles array']) +def test_loads_float(inaccurate_floats_contender, data, benchmark): + data = inaccurate_floats_contender.dumps(doubles) + benchmark(inaccurate_floats_contender.loads, data) # Special case 2: load datetimes as plain strings vs datetime.xxx instances @pytest.mark.benchmark(group='deserialize') -@pytest.mark.parametrize('deserializer', - [rapidjson.loads, - partial(rapidjson.loads, - datetime_mode=rapidjson.DATETIME_MODE_ISO8601)], - ids=['Ignore datetimes', 'Parse datetimes']) -def test_loads_datetimes(deserializer, benchmark): - data = rapidjson.dumps(datetimes, datetime_mode=rapidjson.DATETIME_MODE_ISO8601) - benchmark(deserializer, data) +@pytest.mark.parametrize('data', [datetimes], ids=['256x3 datetimes']) +def test_loads_datetimes(datetimes_loads_contender, data, benchmark): + from rapidjson import dumps, DATETIME_MODE_ISO8601 + data = dumps(data, datetime_mode=DATETIME_MODE_ISO8601) + benchmark(datetimes_loads_contender, data)