diff --git a/benchmarks/README.md b/benchmarks/README.md index ddf7b0f3..be0dc605 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -87,8 +87,9 @@ Method names are `-`, plus `-` for Jelly, which has two APIs | `rdflib-jelly-graph` | Jelly through rdflib's plugin API: `Graph.serialize` | `Graph.parse` | | `rdflib-jelly-stream` | Jelly with pyjelly's flat streaming API, from the list | `parse_jelly_flat` | | `generic-jelly-stream` | The same, with the generic integration's terms instead of rdflib's | `parse_jelly_flat` | +| `rdflib-jelly11-stream`, `generic-jelly11-stream` | The same two, writing Jelly-RDF 1.1 instead of 1.2 | `parse_jelly_flat` | -All Jelly methods use pyjelly's default options: the "big" lookup preset (4000 names, 150 prefixes, 32 datatypes) and frames of 250 rows. The other formats' writers get the dataset's prefixes. +All Jelly methods use pyjelly's default options: the "big" lookup preset (4000 names, 150 prefixes, 32 datatypes) and frames of 1024 rows. The other formats' writers get the dataset's prefixes. ### Tasks diff --git a/benchmarks/measure.py b/benchmarks/measure.py index 6ddf0081..31e4b363 100644 --- a/benchmarks/measure.py +++ b/benchmarks/measure.py @@ -151,11 +151,6 @@ def operation() -> None: def operation() -> int: return int(method.read(written, quads=data.quads)) - # Everything made so far lives until the end. Freezing it keeps the garbage - # collector from going through it again and again during the measurement. - gc.collect() - gc.freeze() - _measure( operation, result, @@ -177,6 +172,14 @@ def _measure( # noqa: PLR0913 profile: Path | None, ) -> None: """Run the warmup and measured iterations, and fill in the result.""" + # All objects allocated up to this point will be needed until the end + # of the benchmark. We freeze it to keep the GC from going through it + # again and again during the measurement. PyPy has no gc.freeze(), + # and doesn't need it: its collector doesn't go through old objects + # again unless they change. + gc.collect() + if hasattr(gc, "freeze"): + gc.freeze() result.warmup = [ _iteration(operation, result.statements, seconds) for _ in range(warmup) ] @@ -212,6 +215,7 @@ def environment() -> dict[str, Any]: "platform": platform.platform(), "machine": platform.machine(), "cpu_affinity": sorted(affinity) if affinity else None, + "pyjelly": importlib.metadata.version("pyjelly"), "pyjelly_commit": commit, "pyjelly_compiled_with_mypyc": compiled, "pyjelly_max_jelly_version": MAX_VERSION, diff --git a/benchmarks/methods.py b/benchmarks/methods.py index 7d08204c..0a906057 100644 --- a/benchmarks/methods.py +++ b/benchmarks/methods.py @@ -28,6 +28,8 @@ from pyjelly.integrations.generic import serialize as generic_serialize from pyjelly.integrations.rdflib import parse as rdflib_parse from pyjelly.integrations.rdflib import serialize as rdflib_serialize +from pyjelly.options import StreamParameters +from pyjelly.serialize.streams import SerializerOptions class NullOutput(io.BytesIO): @@ -254,12 +256,14 @@ class PyjellyFlat(Method): Jelly with pyjelly's flat streaming API, using pyjelly's default options. `integration` is "rdflib" for statements made of rdflib terms, or "generic" - for pyjelly's own terms. + for pyjelly's own terms. With `version`, the stream is written in that protocol + version instead of the default one, e.g. 2 for Jelly-RDF 1.1. """ - def __init__(self, integration: str) -> None: - self.name = f"{integration}-jelly-stream" + def __init__(self, integration: str, version: int | None = None) -> None: + self.name = f"{integration}-jelly{'11' if version == 2 else ''}-stream" # noqa: PLR2004 self.generic = integration == "generic" + self.version = version @override def statements(self, data: BenchData) -> list[Any]: @@ -269,10 +273,13 @@ def statements(self, data: BenchData) -> list[Any]: def write(self, data: BenchData, out: IO[bytes]) -> None: # The API takes a generator, so the list is wrapped in one statements = (statement for statement in self.statements(data)) + options = None + if self.version is not None: + options = SerializerOptions(params=StreamParameters(version=self.version)) if self.generic: - generic_serialize.flat_stream_to_file(statements, out) + generic_serialize.flat_stream_to_file(statements, out, options) else: - rdflib_serialize.flat_stream_to_file(statements, out) + rdflib_serialize.flat_stream_to_file(statements, out, options) def _parse(self, data: bytes) -> Iterable[Any]: if self.generic: @@ -305,6 +312,9 @@ def read_all(self, data: bytes, *, quads: bool) -> list[tuple[Any, ...]]: # Jelly through pyjelly's flat streaming API PyjellyFlat("rdflib"), PyjellyFlat("generic"), + # The same, writing Jelly-RDF 1.1, to compare it with 1.2 + PyjellyFlat("rdflib", version=2), + PyjellyFlat("generic", version=2), ) } diff --git a/benchmarks/suites.py b/benchmarks/suites.py index 3beab54e..2c069684 100644 --- a/benchmarks/suites.py +++ b/benchmarks/suites.py @@ -6,9 +6,8 @@ from dataclasses import dataclass from typing import Any -from benchmarks import data, sparql_data +from benchmarks import data from benchmarks.methods import METHODS, round_trip -from benchmarks.sparql_methods import SPARQL_METHODS, sparql_round_trip @dataclass(frozen=True) @@ -33,7 +32,17 @@ class Suite: data.load, round_trip, ), - "sparql": Suite( +} + +try: + from benchmarks import sparql_data + from benchmarks.sparql_methods import SPARQL_METHODS, sparql_round_trip +except ImportError: + # pyjelly 0.8 and older have no Jelly-SPARQL, but the rdf suite still runs on + # them, to compare them with the current version + pass +else: + SUITES["sparql"] = Suite( "sparql", "cells", sparql_data.DATASETS, @@ -41,5 +50,4 @@ class Suite: SPARQL_METHODS, sparql_data.load, sparql_round_trip, - ), -} + ) diff --git a/pyjelly/serialize/flows.py b/pyjelly/serialize/flows.py index 8fb5f785..10e8237a 100644 --- a/pyjelly/serialize/flows.py +++ b/pyjelly/serialize/flows.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: from pyjelly.serialize.columns import ColumnBatch -DEFAULT_FRAME_SIZE = 250 +DEFAULT_FRAME_SIZE = 1024 class FrameFlow: