In brief🔗
Neither engine wins everywhere. VOS is faster on most queries, and QLever is far faster on a group of queries such as whole-dataset counts. The two reports, built on two VOS builds, tell the same story.
VOS is faster on most queries
VOS has the lower time on 70 queries in both reports. QLever has the lower time on 35.
QLever’s largest lead
number-of-subjects, a whole-dataset count: QLever takes 0.008 s, VOS 490.8 s.
VOS’s largest lead
exists-join-large-small: VOS takes 0.011 s, QLever 1.797 s.
Largest shift between builds
No VOS query that takes over a second moved by more than 16% between the two builds.
Why compare these two🔗
QLever is free, open source and fast on large analytical queries, which makes it a natural point of comparison for other engines.
What QLever is built for
- Fast analytical queries over a large, mostly unchanging dataset.
- A compact, read-only index, built once.
- Answers to some questions read straight from that index.
What Virtuoso is built for
- Multi-model data management covering both tables and graphs.
- DDL and DML operations using SQL and/or SPARQL across respective relational data.
- ACID compliance, since it is a full blown DBMS.
- Support for multiple protocols associated with a variety of open standards.
These are different design goals, both reasonable. A fair comparison therefore says which workload it measures, and the rest of this article does that.
The road to these reports🔗
The steps that led to these reports, in date order.
- 12 Nov 2025
A query without a starting point
A support case (945) showed that a transitive-closure query with nothing to anchor it fails in Virtuoso with an error that gives guidance. Adding a filter that supplies a starting point makes it work. It was noted that QLever rewrites such queries to find a starting point itself, and that a benchmark scores Virtuoso’s error as a failure.
- 15 Dec 2025
Looking at the wider context
This entry considered what the research community looks for: a free store with high performance, on which services and tools can be built. It noted that Virtuoso offers much more than an RDF engine, and that this breadth makes its place in that picture different from a single-purpose engine’s.
- 28 May 2026
Another project compares itself with QLever
A memo in another project’s GitHub repository looks at that project’s comparison with QLever. QLever is the explicit subject, and Virtuoso comes into it only indirectly, as one of the engines such comparisons usually include.
The memo’s title points to its theme, the assumptions built into this kind of comparison: that read-heavy analytics is the main use, that the benchmark queries are representative, and that finishing queries matters more than predictable operation. An engine built for mixed workloads, SQL interoperability and transactional behaviour is then measured against criteria it was not designed around. This description rests on the memo’s title and a reading of it, not on a quotation.
- 26 Sep 2026
What the published benchmarks configured
An audit, with references to a pinned commit of the Virtuoso source, found that QLever’s DBLP evaluation ran Virtuoso mostly on shipped defaults: 4 threads per query on a 16-core machine, vector sizing off, one loader on one file, and a misspelled
ResultsSetMaxRowskey that is silently ignored. It also found real engine differences that configuration cannot close. The configuration or design section summarises both. - 26 Sep 2026
Open Triplestore compares itself with QLever
Open Triplestore is another project that compares itself with QLever, and Virtuoso comes into it only indirectly. Its engine, Oxigraph, runs each query on a single thread. To come within 1.1–1.7× of QLever on six aggregate queries over 501 thousand triples, it added several layers on top, including in-memory copies of the data that let aggregates run in parallel.
It also set out what it found unsuitable in QLever: changed datatypes, large results, partial results returned with a 200 status, and a non-standard addition to SPARQL-JSON. It puts the remaining gap, under 2×, down to columnar dictionary IDs and sorted-permutation merge joins.
Virtuoso already has built-in counterparts for most of what Open Triplestore had to add, such as parallel execution within a query, and settings that control them.
- 5 Oct 2026
Test Machine Setup
VOS and QLever ran the 105 DBLP queries on one machine: two Xeon E5-2630 processors, 192 GB of memory, CentOS 6.2 and ext2 on SSDs. The latest-build report records that the runs started under
numactl --interleave=all, a memory-placement setting explained below. The two reports below present these results.
The two reports🔗
Both are interactive, single-page reports with the same layout and the same 105 queries. They differ in the VOS build behind the numbers. Each link below opens the section named.
Virtuoso Open Source (VOS Develop) vs QLever (with NUMA)
The edition that goes public, based on the latest VOS build, with the machine, run conditions and VOS configuration listed.
Preview the report here
Virtuoso Open Source (VOS) vs QLever (without NUMA)
The same report on the numbers from the current official VOS build. It lists the same machine and VOS configuration. Other run conditions, including memory placement, are not stated for this data set.
Preview the report here
What was measured🔗
Times are in seconds; the lower time wins. The full per-query view is in each report’s Who wins where section: with NUMA and without NUMA.
Which engine is faster, query by query
VOSQLever
One query, number-of-objects, timed out on VOS in both builds, so it counts as a QLever win.
How the machine’s memory layout shapes the numbers
The test machine has two processor sockets, and each socket has its own memory. This is called NUMA, non-uniform memory access: a processor reads its own socket’s memory fastest, and the other socket’s memory about twice as slowly. The latest-build report’s Reading this objectively section explains the setting in full.
- The setting. The runs started under
numactl --interleave=all, which spreads a program’s memory evenly across both sockets instead of placing it mostly next to the thread that asked for it first. - For the comparison. Every thread of both engines sees about the same mix of near and far memory, so neither engine was given the fast placement or left with the slow one.
- For the absolute times. Interleaving trades the best case for a predictable average. On a single-socket machine, or with memory placed near each thread, times could be lower, and the effect could differ between engines. The reports did not measure that.
- For the two builds. Only the latest-build report states this setting. Memory placement is therefore one more condition that could separate the two sets of numbers, beside the build itself and run-to-run variation.
The two VOS builds agree closely
95 of the 105 VOS times differ between the builds, but only slightly. None changed by more than 1.7×, and none above one second changed by more than 16%. Two queries change winner, both by a margin of about a tenth of a second or less: join-2-small-large, which VOS wins in the latest build and QLever wins in the original, and date-year, the reverse. QLever’s own times are the same in both, apart from rounding.
The numbers cannot tell whether the differences come from the build or from run-to-run variation. The original data set does not state its other run conditions, including memory placement.
Why some answers are instant🔗
QLever’s biggest wins are whole-dataset counts, a language filter and prefix regex queries. The audit credits precomputed per-predicate metadata for the count wins. Here is what that means, and why Virtuoso’s design makes it harder.
QLever: a read-only index, built once
- While building the index, QLever records facts about the data, such as how many subjects, predicates and objects it holds.
- It keeps the data sorted in several orders. A search for text that starts with certain letters, or for labels in one language, jumps straight to the right place in a sorted list.
- Because the index is read-only, those stored facts stay true until the index is rebuilt. A count is a lookup.
- The audit notes that this is also why QLever does not offer transactional updates, access control or reasoning on the same terms.
Virtuoso: data that can change at any moment
- Atomic. A change completes or is undone, so no half change is left behind.
- Consistent. Each committed change leaves the data valid.
- Isolated. Readers and writers do not see each other’s unfinished work. Virtuoso uses row locks, with four isolation levels; the default is repeatable read.
- Durable. Confirmed changes are written to a log and replayed after a crash.
What this means for a count
A stored total is only correct if every insert and delete updates it as part of the same transaction, and a rolled-back transaction leaves it untouched. Two readers working under different isolation levels may also be entitled to see different totals at the same moment. Virtuoso therefore works out a count from the live index under the reader’s own transaction, which is correct for each reader and costs a scan.
That is the trade. QLever answers number-of-subjects in 0.008 s by reading a stored number; VOS takes 490.8 s because it counts from the live data. The audit sketches a middle path, per-predicate counts that are kept exact and updated at commit, but it is not implemented or measured. It adds that distinct counts cannot be kept by simple addition and subtraction, and would have to come from the existing distinct indexes.
The same trade shows in the index. The audit puts Virtuoso’s index at 13 GB against QLever’s 8 GB in QLever’s DBLP evaluation, and says part of the gap pays for transactional updates, access control and reasoning.
The report’s Why the numbers look like this section covers the index explanation. The ACID points come from OpenLink’s VOSEngine and VOSDBFAQ pages, which document VOS, so details may differ in newer versions.
Configuration or design🔗
The September audit sorts the criticisms of Virtuoso in published comparisons into those a configuration change settles and those that need engine work.
Mostly configuration
- Four threads per query on a 16-core machine, instead of 16.
- Vector sizing left off, and the starting batch size 10 times below the compiled default.
- A misspelled result-cap key, silently ignored.
- Queries refused on cost estimate, and partial results, both policy settings a benchmark should switch off.
- One loader on one input file, so the load ran on a single thread.
- Single runs from a cold start, where QLever’s cache was cleared and the others’ were not.
Real engine differences
- Large results over HTTP. The endpoint builds the whole result in memory, which caps one response at about 2.1 million rows.
- Transitive paths. Virtuoso kept duplicate routes where the standard asks for each pair once, and needed a starting point. This is handled in VOS 7 Develop, to be merged into the upcoming VOS release.
- Predicate-level aggregates. QLever keeps per-predicate metadata; Virtuoso scans. The audit reports 1.48 s against 0.01 s in QLever’s DBLP evaluation.
- Index size. Part of the gap pays for updates, access control and reasoning.
Related🔗
Live benchmark reports on the URIBurner weblog
- Virtuoso Open Source (VOS Develop) vs QLever (with NUMA)
- Virtuoso Open Source (VOS) vs QLever (without NUMA) · original, current official build