diff --git a/benchmarking/format_results.py b/benchmarking/format_results.py index 9c77206..aab4c92 100644 --- a/benchmarking/format_results.py +++ b/benchmarking/format_results.py @@ -33,7 +33,7 @@ def most_modern_result(self): non_exception_results = [(_python_version, result) for _python_version, result in self.data.items() if result.exception is None] return sorted(non_exception_results, key=lambda kvp: kvp[0], reverse=True)[0][1] -FILENAME_REGEX_RAW = r"benchmark_timings_python(\d)(\d\d?).csv" +FILENAME_REGEX_RAW = r"benchmark_timings_python(\d)(\d\d?)(t?).csv" FILENAME_REGEX = re.compile(FILENAME_REGEX_RAW) MODULE_VERSION_FILENAME_REGEX_RAW = r"module_versions_python(\d)(\d\d?).csv" @@ -94,22 +94,23 @@ def load_benchmarking_results(results_directory): try: with open(csv_file, "r") as fin: reader = csv.reader(fin, delimiter=",", quotechar='"') - major, minor, timestamp = next(reader) + major, minor, freethreaded, timestamp = next(reader) major = int(major) minor = int(minor) + freethreaded = freethreaded == "t" timestamps.add(timestamp) for module, _setup, stmt, parse_result, count, time_taken, matched, exception in reader: if module == "hardcoded": continue timing = float(time_taken) / int(count) if exception == "" else None exception = exception if exception != "" else None - results[module][(major, minor)] = Result( + results[module][(major, minor, freethreaded)] = Result( timing, parse_result, exception, matched == "True" ) - python_versions.add((major, minor)) + python_versions.add((major, minor, freethreaded)) calling_code[module] = f"``{stmt.format(timestamp=timestamp)}``" except Exception: print(f"Problem while parsing `{csv_file}`") @@ -133,7 +134,7 @@ def write_benchmarking_results(results_directory, output_file, baseline_module, modern_versions_before_slowdown_summary = 4 writer = pytablewriter.RstGridTableWriter() - formatted_python_versions = [f"Python {major}.{minor}" for major, minor in python_versions_by_modernity] + formatted_python_versions = [f"Python {major}.{minor}{'t' if freethreaded else ''}" for major, minor, freethreaded in python_versions_by_modernity] writer.headers = ["Module"] + (["Call"] if include_call else []) + formatted_python_versions[0:modern_versions_before_slowdown_summary] + [f"Relative slowdown (versus {baseline_module}, latest Python)"] + SPACER_COLUMN + formatted_python_versions[modern_versions_before_slowdown_summary:] writer.type_hints = [pytablewriter.String] * len(writer.headers) diff --git a/benchmarking/perform_comparison.py b/benchmarking/perform_comparison.py index 5eb976b..7ea695e 100644 --- a/benchmarking/perform_comparison.py +++ b/benchmarking/perform_comparison.py @@ -2,6 +2,7 @@ import csv import os import sys +import sysconfig import timeit from datetime import datetime, timedelta @@ -138,7 +139,7 @@ def update_auto_range_counts(filepath, results): def write_results(filepath, timestamp, results): with open(filepath, "w") as fout: writer = csv.writer(fout, delimiter=",", quotechar='"', lineterminator="\n") - writer.writerow([sys.version_info.major, sys.version_info.minor, timestamp]) + writer.writerow([sys.version_info.major, sys.version_info.minor, "t" if sysconfig.get_config_var("Py_GIL_DISABLED") else "", timestamp]) for result in results: writer.writerow(result.to_row()) @@ -204,7 +205,7 @@ def run_tests(timestamp, results_directory, compare_to): update_auto_range_counts(auto_range_count_filepath, results) - results_filepath = os.path.join(results_directory, "benchmark_timings_python{major}{minor}.csv".format(major=sys.version_info.major, minor=sys.version_info.minor)) + results_filepath = os.path.join(results_directory, "benchmark_timings_python{major}{minor}{freethreaded}.csv".format(major=sys.version_info.major, minor=sys.version_info.minor, freethreaded="t" if sysconfig.get_config_var("Py_GIL_DISABLED") else "")) write_results(results_filepath, timestamp, results) module_versions_filepath = os.path.join(results_directory, "module_versions_python{major}{minor}.csv".format(major=sys.version_info.major, minor=sys.version_info.minor)) diff --git a/benchmarking/tox.ini b/benchmarking/tox.ini index 755837e..0b23632 100644 --- a/benchmarking/tox.ini +++ b/benchmarking/tox.ini @@ -1,7 +1,7 @@ [tox] requires = tox>=4 -envlist = py314,py313,py312,py311,py310,py39,py38 +envlist = py314t,py314,py313,py312,py311,py310,py39,py38 setupdir=.. [testenv] diff --git a/module.c b/module.c index 86d0828..e859739 100644 --- a/module.c +++ b/module.c @@ -40,6 +40,9 @@ static PyObject *utc; * 1439 = Zero offset * 1440 - 2878 = Positive offsets [1...1439] */ +#ifdef Py_GIL_DISABLED +static PyMutex tz_cache_lock = {0}; +#endif static PyObject *tz_cache[2879] = {NULL}; #endif @@ -600,13 +603,25 @@ _parse(PyObject *self, PyObject *dtstr, int parse_any_tzinfo, int rfc3339_only) else { #if CISO8601_CACHING_ENABLED tz_index = tzminute + 1439; +#ifdef Py_GIL_DISABLED + PyMutex_Lock(&tz_cache_lock); +#endif if ((tzinfo = tz_cache[tz_index]) == NULL) { tzinfo = new_fixed_offset(60 * tzminute); - if (tzinfo == NULL) /* i.e., PyErr_Occurred() */ + /* i.e., PyErr_Occurred() */ + if (tzinfo == NULL) { +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&tz_cache_lock); +#endif return NULL; + } + tz_cache[tz_index] = tzinfo; } +#ifdef Py_GIL_DISABLED + PyMutex_Unlock(&tz_cache_lock); +#endif #else tzinfo = new_fixed_offset(60 * tzminute); if (tzinfo == NULL) /* i.e., PyErr_Occurred() */ @@ -741,6 +756,10 @@ initciso8601(void) utc = new_fixed_offset(0); #endif +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED); +#endif + // PyMODINIT_FUNC is void in Python 2, returns PyObject* in Python 3 #if PY_MAJOR_VERSION >= 3 return module; diff --git a/setup.py b/setup.py index 57577fe..718e1f6 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ import os +import sysconfig from setuptools import setup, Extension @@ -33,6 +34,7 @@ VERSION = "2.3.3" CISO8601_CACHING_ENABLED = int(os.environ.get('CISO8601_CACHING_ENABLED', '1') == '1') +Py_GIL_DISABLED = sysconfig.get_config_var("Py_GIL_DISABLED") setup( name="ciso8601", @@ -48,6 +50,7 @@ define_macros=[ ("CISO8601_VERSION", VERSION), ("CISO8601_CACHING_ENABLED", CISO8601_CACHING_ENABLED), + ("Py_GIL_DISABLED", Py_GIL_DISABLED), ], ) ], diff --git a/tox.ini b/tox.ini index 1521dbe..4cd214b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] requires = tox>=4 -envlist = {py314,py313,py312,py311,py310,py39,py38}-caching_{enabled,disabled} +envlist = {py314t,py314,py313,py312,py311,py310,py39,py38}-caching_{enabled,disabled} [testenv] package = sdist @@ -11,4 +11,5 @@ setenv = caching_disabled: CISO8601_CACHING_ENABLED = 0 deps = pytz -commands=python -m unittest + unittest-ft +commands=unittest-ft