\section{Comparisons}\label{sec:cmp} This section makes some basic comparison between \expkv\ and other \kv\ packages. The comparisons are really concise, regarding speed, feature range (without listing the features of each package, comparisons are done against the base \expkv\ not counting other packages in \expkvbundle\ that extend it, so \enquote{bigger feature set} might not necessarily be true if everything is included), and bugs and misfeatures. Comparisons of speed are done with a very simple test key and the help of the \pkg{l3benchmark} package. The key and its usage should be equivalent to \begin{enverb}[no-tcb] \protected\ekvdef{test}{height}{\def\myheight{#1}} \ekvsetdef\expkvtest{test} \expkvtest{ height = 6 } \end{enverb} and only the usage of the key, not its definition, is benchmarked. For the impatient, the essence of these comparisons regarding speed and buggy behaviour is contained in \autoref{tab:comp}. As far as I know \expkv\ is the only fully expandable \kv\ parser. I tried to compare \expkv\ to every \kv\ package listed on \href{https://ctan.org/topic/keyval}{CTAN}, however, one might notice that some of those are missing from this list. That's because I didn't get the others to work due to bugs, or because they just provide wrappers around other packages in this list. In this subsection is no benchmark of |\ekvparse| and |\keyval_parse:NNn| contained, as most other packages don't provide equivalent features to my knowledge. |\ekvparse| is slightly faster than |\ekvset|, but keep in mind that it does less. The same is true for |\keyval_parse:NNn| compared to |\keys_set:nn| of \pkg{expl3} (where the difference is much bigger). Comparing just the two, |\ekvparse| is a tad faster than |\keyval_parse:NNn| because of two tests (for empty key names and only a single equal sign) which are omitted. \paragraph{\pkg{keyval}} is the fastest \kv\ package there is and has a minimal feature set with a slightly different way how it handles keys without values compared to \expkv. That might be considered a drawback, as it limits the versatility, but also as an advantage, as it might reduce doubled code. Keep in mind that as soon as someone loads \pkg{xkeyval} the performance of \pkg{keyval} gets replaced by \pkg{xkeyval}'s. Also \pkg{keyval} has a \pmso{bug}feature, which unfortunately can't really be resolved without breaking backwards compatibility for \emph{many} documents, namely it strips braces from the argument before stripping spaces if the outer most braces aren't surrounded by spaces, also it might strip more than one set of braces. Hence all of the following are equivalent in their outcome, though the last two lines should result in something different than the first two: \begin{enverb}[no-tcb] \setkeys{foo}{bar=baz} \setkeys{foo}{bar= {baz}} \setkeys{foo}{bar={ baz}} % should be ` baz' \setkeys{foo}{bar={{baz}}} % should be `{baz}' \end{enverb} \pkg{keyval} doesn't work with non-standard category codes of |=| and |,|. Also if a \kv\ pair contains multiple equals signs outside of braces everything post the first is silently ignored so the following two inputs yield identical outputs: \begin{enverb}[no-tcb,lst=belowskip=0pt] \setkeys{foo}{bar=baz} \setkeys{foo}{bar=baz=and more} \end{enverb} \paragraph{\pkg{xkeyval}} is pretty slow (yet not the slowest), but it provides more functionality, e.g., it has an interface to disable a list of keys, can search multiple sets simultaneously, and has an intriguing mechanism it calls \enquote{Pointers} to save the value of particular keys for later reuse. It contains the same bug as \pkg{keyval} as it has to be compatible with it by design (it replaces \pkg{keyval}'s frontend), but also adds even more cases in which braces are stripped that shouldn't be stripped, worsening the situation. \pkg{xkeyval} does work with non-standard category codes of |=| and |,|, but the used mechanism fails if the input contains a mix of different category codes for the same character. Just like with \pkg{keyval} equals signs after the first and everything after those is ignored. \paragraph{\pkg{ltxkeys}} is no longer compatible with the \LaTeX\ kernel starting with the release 2020-10-01. It is by far the slowest \kv\ package I've tested -- which is funny, because it aims to be ``[\ldots] faster [\ldots] than these earlier packages [referring to \pkg{keyval} and \pkg{xkeyval}].'' It needs more time to parse zero~keys than five of the packages in this comparison need to parse 100~keys. Since it aims to have a bigger feature set than \pkg{xkeyval}, it most definitely also has a bigger feature set than \expkv. Also, it can't parse |\long| input, so as soon as your values contain a |\par|, it'll throw errors. Furthermore, \pkg{ltxkeys} doesn't strip outer braces at all by design, which, imho, is a weird design choice. Some of the more intriguing features (e.g., the |\argpattern| mechanism) didn't work for me. In addition \pkg{ltxkeys} loads \pkg{catoptions} which is known to introduce bugs (e.g., see \url{https://tex.stackexchange.com/questions/461783}). Because it is no longer compatible with the kernel, I stop benchmarking it (so the numbers listed here and in \autoref{tab:comp} regarding \pkg{ltxkeys} were last updated on 2020-10-05). \pkg{ltxkeys} works with non-standard category codes, it also silently ignores any additional equals signs and the following tokens. \paragraph{\pkg{l3keys}} is at the slower end of the midfield yet not unusably slow, but has an, imho, great interface to define keys. It strips \emph{all} outer spaces, even if somehow multiple spaces ended up on either end. It offers more features, but has pretty much been bound to \pkg{expl3} code before. Nowadays the \LaTeX\ kernel has an interface with the macros |\DeclareKeys|, |\SetKeys|, and |\ProcessKeyOptions| that provides access to \pkg{l3keys} from the \LaTeXe\ layer as well as parsing package options with it. Because of the |\ProcessKeyOptions| macro and its features the only two viable options to provide \kv\ options for new projects in my opinion are the kernel's methods and \expkvo\ as those are the only two until now up to my knowledge that support parsing the raw options, and future options. \pkg{l3keys} handles active commas and equals signs fine. Multiple equals signs lead to an error if additional equals signs aren't nested inside of braces, so perfectly predictable behaviour here. \paragraph{\pkg{pgfkeys}} is among the top~4 of speed if one uses |\pgfqkeys| over |\pgfkeys|, else the initialisation parsing the family path takes roughly \SI{43}{\ops} and moves it two spots down the list (so in \autoref{tab:comp} both $p_0$ and $T_0$ would be about \SI{43}{\ops} bigger if |\pgfkeys{|\meta{path}|/.cd,|\meta{keys}|}| was used instead). It has an \emph{enormous} feature set. It stores keys in a way that reminds one of folders in a Unix system which allows interesting features and has other syntactic sugars. It is another package that implements something like the \expnotation\ with less different options though. To get the best performance |\pgfqkeys| was used in the benchmark. It has the same or a very similar bug \pkg{keyval} has. The brace bug (and also the category fragility) can be fixed by \pkg{pgfkeyx}, but this package was last updated in 2012 and it slows down |\pgfkeys| by factor~\num{8}. Also \pkg{pgfkeyx} is no longer compatible with versions of \pkg{pgfkeys} newer than 2020-05-25. \pkg{pgfkeys} silently drops anything after the second unbraced equals sign in a \kv\ pair. \paragraph{\pkg{kvsetkeys} with \pkg{kvdefinekeys}} is in the slower midfield, but it works even if commas and equals have category codes different from 12 (just as some other packages in this list). It has quadratic run-time unlike most other \kv\ implementations which behave linear. The features of the keys are equal to those of \pkg{keyval}, the parser adds handling of unknown keys. \pkg{kvsetkeys} does include any additional equals sign in the value. But any active equals sign is turned into one of category code 12 if it's not nested in braces. Also spaces around superfluous equals signs are stripped. So the following all result in the same: \begin{enverb}[no-tcb,lst=belowskip=0pt] \kvsetkeys{foo}{bar=baz=morebaz} \kvsetkeys{foo}{bar=baz =morebaz} \kvsetkeys{foo}{bar=baz= morebaz} \kvsetkeys{foo}{bar=baz = morebaz} \end{enverb} \paragraph{\pkg{options}} is in the midfield of speed. It is faster per individual key than \pkg{pgfkeys} but has no shortcut like |\pgfqkeys|. It has a much bigger feature set than \expkv. Similar to \pkg{pgfkeys} it uses a folder like structure, makes searching multiple paths easy, incorporates package options and more. It also features a form of expansion control, predefined expansion kinds are limited though one can define additional ones. Unfortunately it also suffers from the premature unbracing bug \pkg{keyval} has. \pkg{options} can't handle non-standard category codes and will silently ignore superfluous equals signs and following tokens. \paragraph{\pkg{simplekv}} is hard to compare because I don't speak French (so I don't understand the documentation). There was an update released on 2020-04-27 which greatly improved the package's performance and added functionality so that it can be used more like most of the other \kv\ packages. Speed wise it is pretty close to \expkv. Regarding unknown keys it got a very interesting behaviour. It doesn't throw an error, but stores the \val\ in a new entry accessible with \cs[no-index]{useKV}. Also if you omit \val\ it stores |true| for that \key. \pkg{simplekv} can't correctly handle non-standard category codes. It silently ignores any unbraced equals sign beyond the first and any following tokens. \paragraph{\protect\yax} is the second slowest package I've tested. It has a pretty strange syntax for the \TeX-world, imho, and again a direct equivalent is hard to define (don't understand me wrong, I don't say I don't like the syntax, quite the contrary, it's just atypical). It has the premature unbracing bug, too. \yax\ features some prefixes one can use to make an assignment use |\edef|, |\gdef| or |\xdef| so has something that comes close to expansion control. Also somehow loading \yax\ broke \pkg{options} for me. The tested definition was: \begin{enverb}[no-tcb] \usepackage{yax} \defactiveparameter yax {\storevalue\myheight yax:height } % setup \setparameterlist{yax}{ height = 6 } % benchmark \end{enverb} This seems important to state as \yax\ supports two different input syntaxes, the tested one was the one closer to traditional \kv\ input. \yax\ won't handle non-standard category codes correctly. Superfluous equals signs end up in the value in an unaltered form (just like with \expkv). \begin{table} \def\fnsym{\textcolor{red!80!black}{*}}% \sisetup{round-precision=1, round-mode=places}% \begingroup \centering \newcommand*\yes{\textcolor{red!80!black} {yes}}^^A \newcommand*\no {\textcolor{green!80!black}{no}}^^A \caption[Comparison of \kv\ packages] {^^A Comparison of \kv\ packages. The packages are ordered from fastest to slowest for one \kv\ pair. Benchmarking was done using \pkg{l3benchmark} and the scripts in the \file{Benchmarks} folder of \href{https://github.com/Skillmon/tex_expkv}{the original \expkv's git repository}. The columns $p_i$ are the polynomial coefficients of a linear fit to the run-time, $p_0$ can be interpreted as the overhead for initialisation and $p_1$ the cost per key. The $T_0$ column is the actual mean ops needed for an empty list argument, as the linear fit doesn't match that point well in general. The column ``BB'' lists whether the parsing is affected by some sort of brace bug, ``CF'' stands for category code fragile and lists whether the parsing breaks with active commas or equal signs.^^A \label{tab:comp}^^A } \begin{tabular} {>{\collectcell\pkg}l<{\endcollectcell}*3{S[table-format=4.1]}ccc} \toprule \rmfamily Package & {$p_1$} & {$p_0$} & {$T_0$}& BB & CF & Date \\ \midrule keyval & 13.557 & 2.202 & 7.185 & \yes & \yes & 2022-05-29 \\ \expkv & 16.669 & 3.132 & 5.836 & \no & \no & 2023-01-10 \\ simplekv & 19.943 & 2.850 & 15.120 & \no & \yes & 2022-10-01 \\ pgfkeys & 24.465 & 2.244 & 10.283 & \yes & \yes & 2021-05-15 \\ options & 23.255 & 16.160 & 20.400 & \yes & \yes & 2015-03-01 \\ kvsetkeys & {\fnsym} & {\fnsym} & 40.360 & \no & \no & 2022-10-05 \\ l3keys & 70.643 & 35.602 & 32.230 & \no & \no & 2022-12-17 \\ xkeyval & 255.906 & 221.276 & 173.400 & \yes & \yes & 2022-06-16 \\ \yax & 438.242 & 131.846 & 114.800 & \yes & \yes & 2010-01-22 \\ ltxkeys & 3400.142 & 4737.958 & 5368.000 & \no & \no & 2012-11-17 \\ \bottomrule \end{tabular} \par \endgroup \medskip \fnsym For \pkg{kvsetkeys} the linear model used for the other packages is a poor fit, \pkg{kvsetkeys} seems to have approximately quadratic run-time, the coefficients of the second degree polynomial fit are $p_2=\num{7.617}$, $p_1=\num{47.727}$, and $p_0=\num{57.988}$. Of course the other packages might not really have linear run-time, but at least from 1~to 20~keys the fits don't seem too bad. If one extrapolates the fits for 100 \kv\ pairs one finds that most of them match pretty well, the exception being \pkg{ltxkeys}, which behaves quadratic as well with $p_2=\num{23.500}$, $p_1=\num{2906.634}$, and $p_0=\num{6547.489}$. \end{table}