<?xml version="1.0" encoding="utf-8"?><article xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0-subset Balisage-1.5" xml:id="HR-23632987-8973">
    <title>Achieving Near-Native Performance in XSLT, XQuery, and XPath Using Host-Language User
        Extension Functions</title>
    <info>
    <confgroup>
      <conftitle>Balisage: The Markup Conference 2026</conftitle>
      <confdates>August 3-7, 2026</confdates>
   </confgroup>
        <abstract>
            <para>XSLT, XQuery, and XPath provide powerful declarative languages for processing XML
                and related data formats <citation>XSLT30</citation> <citation>XQUERY31</citation>. 
                However, some workloads involve computationally intensive
                operations or integration requirements that are more naturally expressed in a host
                programming language.</para>

            <para>Modern XML processors allow user-defined extension functions to be implemented in
                host languages (e.g., in Java, C#, JS, C++, Python and PHP) and invoked directly from
                XSLT, XQuery, and XPath expressions <citation>SAXON</citation>
                    <citation>BASEX</citation>.</para>

            <para>This paper investigates the performance and practical benefits of using
                host-language user extension functions where necessary as an alternative to
                implementing equivalent logic directly in XSLT, XQuery, or XPath. As a case study,
                we implement and benchmark equivalent computational workloads using extension
                functions written in Java, C++ and Python. We examine the trade-offs between
                declarative purity, execution performance, and integration flexibility, including
                the overhead of crossing language boundaries and the circumstances under which
                host-language implementations can outperform equivalent XML-language
                expressions.</para>

            <para>The results demonstrate that carefully designed extension functions can achieve
                performance approaching that of the host language itself while remaining fully
                integrated within XML processing. These findings suggest that extension functions
                provide a valuable mechanism for addressing performance-critical and
                integration-oriented requirements without abandoning the declarative programming
                model offered by XSLT, XQuery, and XPath.</para>


        </abstract>
        <author>
            <personname>
                <firstname>O'Neil</firstname>
                <surname>Delpratt</surname>
            </personname>
            <personblurb>
                <para>O'Neil Delpratt joined Saxonica from a research project at the University of
                    Leicester in 2010. He is a co-developer of the Saxon product, with specific
                    responsibility for Saxon on .NET, and the C/C++, PHP and Python product SaxonC.
                    Before joining Saxonica, he completed his post-graduate studies at the
                    University of Leicester. His thesis title was "In-memory Representations of XML
                    documents" which coincided with a C++ software development of a memory efficient
                    DOM implementation, called Succinct DOM. O'Neil regularly publishes and presents
                    papers at various XML conferences, and was an invited expert on the W3C XQuery
                    Working Group.</para>
            </personblurb>
            <affiliation>
                <orgname>Saxonica</orgname>
            </affiliation>
            <email>oneil@saxonica.com</email>
        </author>
<legalnotice><para>Copyright © 2026 by the author. Used with permission.</para></legalnotice>
        <keywordset role="author">
            <keyword>XSLT</keyword>
            <keyword>XQuery</keyword>
            <keyword>XPath</keyword>
            <keyword>Java</keyword>
            <keyword>C++</keyword>
            <keyword>Python</keyword>
            <keyword>PHP</keyword>
            <keyword>API</keyword>
            <keyword>GRAALVM</keyword>
        </keywordset>
    </info>
    <section>
        <title>Introduction and Motivation</title>

        <para>The motivation for this work stems in part from Cython, a superset of Python that
            enables developers to achieve performance closer to that of C while retaining much of
            the Python programming model <citation>CYTHON</citation>.</para>
        <para>The performance gains of Cython are achieved through several mechanisms. Firstly,
            Cython allows variables, function parameters and return values to be declared using
            static C types, reducing the overhead associated with Python's dynamic type system.
            Secondly, statically typed values can be compiled directly in C, avoiding runtime type
            checks and object allocations. Thirdly, Cython can invoke C functions and libraries
            directly, bypassing the Python interpreter for computationally intensive tasks. The
            generated code remains interoperable with the Python runtime and object model.</para>

        <para>The final point mentioned above is particularly relevant to this work because it
            parallels the way SaxonC integrates Python extension functions with XSLT, XQuery, and
            XPath (Details on the Saxon variants, see <xref linkend="appendix-saxon-variants"/>). In both cases, computationally intensive
            operations can be delegated to Python code while remaining accessible from a
            higher-level programming environment.</para>

        <para>There are other tools that mix host language functionity too. For example, the XForms
            implementation Saxon-Forms <citation>SAXONFORMS</citation> was built using SaxonJS to
            run in web browsers. The extensibility of Saxon-Forms is important for certain
            applications, where some XForms functions not in the XPath specification are implemented
            as user-defined functions written directly in JavaScript. The paper also describes the
            use of application logic implemented in JavaScript to meet the needs of its users. These
            functions can then be imported and invoked from XPath expressions used within XForms
            applications.</para>

        <para>This raises an interesting question for XML technologies: can host-language extension
            functions provide similar benefits for XSLT, XQuery, and XPath applications, allowing
            developers to preserve a declarative XML-processing architecture while selectively
            benefiting from the performance characteristics of native languages?</para>

        <para>The experimental analysis presented in this paper focuses on SaxonJ and SaxonC (specifically the commercial products - named as EE). 
            SaxonJ is implemented entirely in Java and is designed to run on a traditional JVM. SaxonC, by
            contrast, is built using GraalVM Native Image technology (AOT compiler) <citation>GRAALVM</citation>, a 
            JVM implementation capable of compiling
            Java applications ahead of time into native code and packaging them as shared
            libraries.</para>

        <para>Effectively, a lightweight VM runtime executes within the native library, allowing
            native applications to invoke functionality implemented in Java. This enables applications 
            written in other languages, such as in C and C++,
            to call Saxon without requiring a separate Java installation.</para>

        <para>The resulting binary includes the compiled SaxonJ code together with the required JDK
            libraries and runtime components needed to execute the native image. Creating a native
            image offers several advantages, including a C/C++ API, faster startup times, lower
            resource usage, greater flexibility when integrating with other programming languages,
            and simplified deployment and packaging. In addition to its C and C++ APIs, SaxonC
            provides language bindings for Python and PHP.</para>

        <para>Before presenting our experimental analysis and results, we first describe how
            extension functions are implemented in Saxon. The discussion focuses on SaxonJ for Java
            and SaxonC, which adopts a different approach to integrating and supporting host
            languages such as C++, Python, and PHP. We then detail our experimental design,
            performance analysis and discussion of results then finally conclusion on findings.</para>
    </section>


    <section>
        <title>User Extension Function Implementations</title>
        <para>Extension functions allow code written in a host programming language (for example: Java, C++,
            Python or PHP) to be invoked directly from a function call in an XPath expression
            (whether in XSLT, XQuery, or pure XPath). For this to work the Saxon processor needs to
            be able to locate the executable code corresponding to a particular function name. The
            function arguments need also to be available and are passed as either XDM types or converted to the host language type
            system, with the opposite conversion applied to the return value of the function.</para>

        <section>
            <title>Why extension functions?</title>
            <para>Before describing the various user extension function implementations,<footnote>
                    <para>XQuery calls them external functions, XSLT extension functions. There is
                        no technical difference.</para>
                </footnote> it is worth considering why extension functions are useful and why a
                developer might choose to use them. One reason is that modern XML processors, such as Saxon or BaseX, 
                provide mechanisms for extending XSLT, XQuery, and XPath
                with functions written in a host programming language. This flexibility allows XML
                technologies to be integrated more easily into larger software systems and enables
                developers to reuse existing libraries and functionality written in languages such
                as Java, C++, Python, and PHP.</para>

            <para>Another motivation is performance. Certain operations are more naturally expressed
                and executed in imperative programming languages than in XSLT or XPath. For example,
                computationally intensive algorithms, numerical calculations, bitwise operations, or
                calls to specialised native libraries may incur significant overhead when
                implemented purely in XSLT or XPath. In such cases, implementing the computationally
                intensive portion of the workload as an extension function can provide substantial
                performance benefits while still allowing the overall application logic to be
                expressed in XSLT, XQuery, or XPath.</para>
        </section>

        <section>
            <title>Implementation in SaxonJ (Java)</title>

            <para>In SaxonJ at a high level, the implementation works as follows
                    <citation>SAXONJ-EX</citation>: The user creates a Java class that extends
                    <code>ExtensionFunctionDefinition</code> class:<footnote>
                    <para>Reflexive extension functions are also available but not discussed in this
                        paper.</para>
                </footnote> <orderedlist>
                    <listitem>
                        <para>Class declares: <itemizedlist>
                                <listitem>
                                    <para>The function name (<code>StructuredQName</code>)</para>
                                </listitem>
                                <listitem>
                                    <para>Argument types</para>
                                </listitem>
                                <listitem>
                                    <para>Result type</para>
                                </listitem>
                                <listitem>
                                    <para>Cardinality</para>
                                </listitem>
                            </itemizedlist></para>
                    </listitem>
                    <listitem>
                        <para>The user then has to register the function with the Saxon processor
                            configuration.</para>
                    </listitem>
                    <listitem>
                        <para>When a call on the extension function is encountered during XPath,
                            XSLT, or XQuery execution, Saxon invokes the corresponding
                                <code>ExtensionFunctionCall</code> object.</para>
                    </listitem>
                    <listitem>
                        <para>Arguments to the call function are passed as an array of objects of
                            the XDM data model,<footnote>
                                <para>The XDM (XML Data Model) is the W3C standard data model shared
                                    between XPath, XQuery, and XSLT. It is defined for the
                                    representation of documents, nodes, atomic values, functions,
                                    maps, arrays, and sequences that are manipulated by these
                                    languages.</para>
                            </footnote> and the return type must also be an XDM value.</para>
                    </listitem>
                </orderedlist>
            </para>
        </section>


        <section>
            <title>Implementation in SaxonC (C++/Python/PHP)</title>
            <para>The implementation of user-defined extension functions in SaxonC builds upon the
                SaxonJ implementation. It extends the ExtensionFunctionDefinition class; however, to
                support functions written across language boundaries for C++, Python, and PHP, the
                underlying implementation differs significantly
                <citation>SAXONC-EX</citation>.</para>

            <para>The user function registration is done through the <code>SaxonProcessor</code> using a JSON
                description that specifies the function namespace, name, function arity, argument
                types, and return type.</para>

            <para>The host-language function is supplied to Saxon as a function pointer. When an
                XPath, XSLT, or XQuery executable invokes the extension function, GraalVM is used to
                call the user-supplied function through a thin C layer, which dispatches the
                callback to the corresponding C++, Python, or PHP implementation. During this
                process, the function arguments are converted to the appropriate host-language
                primitive type system, if possible. For the XDM tree node types these cannot be converted to a primitive
                type therefore we leave these as wrapper objects in C++, Python or PHP. The return value is 
                converted back into an XDM type so that it can be consumed
                by the Saxon processing engine.</para>
            <para>See example below which shows in C++ the setup of a user function in XSLT. Its a
                similar approach in Python and PHP. But in Python the user has the additional option
                to request conversion of XDM types to primitive python types where possible. Also in
                Python the method argument has a keyword argument to get access to the
                    <code>PySaxonProcessor</code> object used for the configuration. </para>
            <para>This method illustrates a CPU intensive task for calculating the square root of a
                set of numbers iterated a certain number of times. The arguments match those defined
                in the signature (see below) and are passed in the XSLT/XQuery/XPath call:
<programlisting xml:space="preserve">
XdmValue * calculate(int argc, XdmValue ** argv) {

    long iterations =
        ((XdmAtomicValue *)argv[0]-&gt;getHead())-&gt;getLongValue();

    double otherData =
        ((XdmAtomicValue *)argv[1]-&gt;getHead())-&gt;getDoubleValue();

    double result = 0.0;

    for (long i = 1; i &lt;= iterations; i++) {
        result += std::sqrt(static_cast&lt;double&gt;(i)) + otherData;
    }
    return processor-&gt;makeDoubleValue(result);
}
</programlisting></para>

            <para>The registration of the <code>calculate</code> user function is given below.
                Notice the pointer to the calculate function is passed to the <code>SaxonProcessor</code> which
                is used in the round-tripping to call the function when required in the XSLT, XPath
                or in XQuery:
<programlisting xml:space="preserve">
proc-&gt;registerUserFunction("www.host-lang-func.com", &amp;calculate,
    "{ \"calculate\": {"
    "      \"as\": \"xs:double\","
    "      \"param\": ["
    "        \"xs:integer\", "
    "        \"xs:integer\""
    "      ],"
    "      \"arity\": [2]"
    "    }"
    "}");
</programlisting>
            </para>
            <para> The XSLT code that calls the C++ calculate function is shown below.<footnote>
                    <para>When executing code N times for performance measurement, it's important to
                        ensure that it doesn't get "loop lifted" by the optimizer. That's why the
                        call in this case accesses the loop counter.</para>
                </footnote> The namespace used to register the function must match the namespace
                declared in the XSLT; otherwise, the function will not be found. For example:
<programlisting xml:space="preserve">
    xmlns:func="www.host-lang-func.com"
</programlisting> The
                namespace URI specified during function registration must be identical to the URI
                used in the XSLT declaration. 
<programlisting xml:space="preserve">
    &lt;xsl:template name="funcCall"&gt;
        &lt;results&gt;
            &lt;!-- Repeat the expensive function 100 times --&gt;
            &lt;xsl:for-each select="1 to $rpt"&gt;
                &lt;run number="{.}"&gt;
                    &lt;xsl:value-of select="func:calculate($input, .)"/&gt;
                &lt;/run&gt;
            &lt;/xsl:for-each&gt;
        &lt;/results&gt;
    &lt;/xsl:template&gt;
</programlisting>
            </para>

        </section>

    </section>

    <section>
        <title>Experimental anaylsis</title>


        <para>In this section, we present a number of examples. For each of them we compare the
            execution time performance of a pure XSLT stylesheet that uses the
                <code>xsl:function</code> instruction to perform a task with an equivalent modified
            XSLT stylesheet that perform the same task using a user-defined extension function. To
            broaden the scope of the experiments, we execute the stylesheets in Java, C++, and
            Python implementations. This is achieved through the SaxonJ s9api and SaxonC APIs, with
            equivalent user extension functions implemented in each host language.</para>
        <para>The experiments are designed to address several questions. Is pure XSLT faster in all
            cases? Can user extension functions be used to improve performance for certain classes
            of tasks? What overheads are introduced when invoking host-language functions from XSLT,
            and under what circumstances should such functions be avoided? By examining both
            computational and XML-processing workloads, we aim to identify the situations in which
            extension functions provide the greatest benefit and the trade-offs involved in their
            use.</para>

        <section>
            <title>Setup</title>
            <para>The experiments were conducted using the release version 13.1 for both SaxonJ and
                SaxonC. At the time of writing, this version was a pre-release. The test machine was
                an Apple MacBook Pro equipped with an M3 Max processor and 64 GB of memory. </para>
            <para>The performance analysis is based on four benchmark scenarios, each implemented
                both as a pure XSLT <code>xsl:function</code> and as an equivalent user-defined
                extension function written in Java, C++, and Python: </para>
            <para><orderedlist>
                    <listitem>
                        <para>
                            <code>math:sqrt(number)</code> — performs square-root calculations over
                            10 million numbers, repeated 100 times. This benchmark is intended to
                            represent a CPU-intensive computational workload. </para>
                        <para><itemizedlist>
                                <listitem>
                                    <para>XSLT function: 
<programlisting xml:space="preserve">
    &lt;xsl:function name="xf:calculate" as="xs:double"&gt;
        &lt;xsl:param name="iterations" as="xs:integer"/&gt;
        &lt;xsl:param name="context" as="xs:integer"/&gt;

        &lt;xsl:sequence select="
            sum(
                for $i in 1 to $iterations
                return math:sqrt($i) + $context
            )
        "/&gt;
    &lt;/xsl:function&gt;&gt;
</programlisting></para>
                                </listitem>
                                <listitem>
                                    <para>The C++ function: 
<programlisting xml:space="preserve">
XdmValue * calculate(int argc, XdmValue ** argv) {

    long iterations =
        ((XdmAtomicValue *)argv[0]-&gt;getHead())-&gt;getLongValue();

    double context =
        ((XdmAtomicValue *)argv[1]-&gt;getHead())-&gt;getDoubleValue();

    double result = 0.0;

    for (long i = 1; i &lt;= iterations; i++) {
        result += std::sqrt(static_cast&lt;double&gt;(i)) + context;
    }

    return processor-&gt;makeDoubleValue(result);
}</programlisting></para>
                                    <para>The Java code is similar: 
<programlisting xml:space="preserve">
public XdmValue call(XdmValue[] arguments) throws SaxonApiException {
    long iterations = ((XdmAtomicValue) arguments[0].itemAt(0))
                                                    .getLongValue();
    long context = ((XdmAtomicValue) arguments[1].itemAt(0))
                                                    .getLongValue();
    double total = 0.0;

    for (int i = 1; i &lt;= iterations; i++) {
        total += Math.sqrt(i) + context;
    }

    return new XdmAtomicValue(total);
}
</programlisting></para>

                                    <para>The equivalent Python code is given below. For the Python
                                        extension we do the following: 
<programlisting xml:space="preserve">
def calculate(iterations, context, **kwargs):

    result = sum((math.sqrt(i) + context) 
                    for i in range(1, iterations + 1))
    return result
</programlisting></para>
                                </listitem>

                            </itemizedlist></para>
                    </listitem>
                    <listitem>
                        <para>
                            <code>abs(number)</code> — performs a simple absolute-value calculation
                            repeated 100,000 times. This benchmark is intended
                            to measure the overhead associated with calling an extension function
                            for a relatively inexpensive operation.</para>
                        <para><itemizedlist>
                                <listitem>
                                    <para>XSLT function: 
<programlisting xml:space="preserve">
    &lt;xsl:function name="f:calculate" as="xs:double"&gt;
        &lt;xsl:param name="iterations" as="xs:integer"/&gt;
        &lt;xsl:sequence select="abs($value)"/&gt;
    &lt;/xsl:function&gt;
</programlisting></para>
                                </listitem>
                                <listitem>
                                    <para>Java function: <code>new
                                            XdmAtomicValue(Math.abs((double)value))</code>.
                                        Similarly in C++ this would be
                                            <code>processor-&gt;makeDoubleValue(std::abs((double)value))</code></para>
                                </listitem>

                            </itemizedlist></para>

                    </listitem>
                    <listitem>
                        <para>Tree navigation using a predicate on a 10 MB XMark XML document. The
                            <code>$person</code> variable is given as a string parameter to the
                            stylesheet.</para>
                        <para><itemizedlist>
                                <listitem>
                                    <para>XSLT function:
<programlisting xml:space="preserve">
    $node//person[name = $person]
</programlisting>
</para>
                                </listitem>
                                <listitem>
                                    <para>In Java, C++ and Python we would need to traverse the tree
                                        using the XDM node API and look for the person with "name"
                                        element that matches a certain string name.</para>
                                </listitem>

                            </itemizedlist></para>
                    </listitem>
                    <listitem>
                        <para>Tree navigation and string-length computation over all nodes in a 100
                            KB XMark XML document.</para>
                        <para><itemizedlist>
                                <listitem>
                                    <para>XSLT function: 
<programlisting xml:space="preserve">
    &lt;xsl:function name="xf:process-node" as="xs:integer"&gt;
        &lt;xsl:param name="node" as="node()"/&gt;
        &lt;xsl:param name="iterations" as="xs:integer"/&gt;

        &lt;xsl:sequence select="
        sum(
            for $i in 1 to $iterations
            return
                sum(
                    for $n in $node/descendant-or-self::node()
                    return string-length(string($n))+$i
                )
        )
    "/&gt;
    &lt;/xsl:function&gt;
</programlisting>
</para>
                                </listitem>
                                <listitem>
                                    <para>In Java, C++ and Python we can use a recursive function to
                                        get the string-lengths on the XDM node in the tree.</para>
                                </listitem>

                            </itemizedlist></para>
                    </listitem>
                </orderedlist></para>
        </section>


        <section>
            <title>Running Times</title>
            <para>The following table shows the execution times, measured in seconds.</para>

           <!-- TRG note:  retagged OASIS table as HTML table -->
            <!--<table frame="all">-->
            <table frame="box" rules="all">
                    <caption><para>Benchmark Results</para></caption>
                    <!--<tgroup cols="7">-->
                        <thead align="center">
                            <tr>
                                <th/>
                                <th>SaxonJ - XSLT (Java)</th>
                                <th>SaxonJ-XSLT (Java+Func)</th>
                                <th>SaxonC-XSLT (Python)</th>
                                <th>SaxonC-XSLT (Python+Func)</th>
                                <th>SaxonC-XSLT (C++)</th>
                                <th>SaxonC-XSLT (C++ +Func)</th>
                            </tr>
                        </thead>
                        <tbody align="center">
                            <tr>
                                <td align="left">math:sqrt() and sum calculation</td>
                                <td>9s</td>
                                <td>0.63s</td>
                                <td>71s</td>
                                <td>55s</td>
                                <td>71s</td>
                                <td>0.76s</td>
                            </tr>
                            <tr>
                                <td align="left">abs()</td>
                                <td>0.052s</td>
                                <td>0.066s</td>
                                <td>0.08s</td>
                                <td>0.23s</td>
                                <td>0.086s</td>
                                <td>0.21s</td>
                            </tr>
                            <tr>
                                <td align="left">Tree-walk and string length calc</td>
                                <td>7.2s</td>
                                <td>7.6s</td>
                                <td>28.1s</td>
                                <td>77.8s</td>
                                <td>23s</td>
                                <td>202s</td>
                            </tr>
                            <tr>
                                <td align="left">Tree-walk with predicate</td>
                                <td>0.022s</td>
                                <td>0.042s</td>
                                <td>0.017s</td>
                                <td>0.342s</td>
                                <td>2.59s</td>
                                <td>2.64s</td>
                            </tr>
                        </tbody>
                    <!--</tgroup>-->
                </table>
        </section>

        <section>
            <title>Discussion</title>
            <para>It is worth mentioning again that SaxonC is built from SaxonJ using GraalVM
                native-image technology to produce native code. Consequently, we expect some
                overhead when invoking callbacks in SaxonC, as the C++ and Python APIs must
                ultimately interact with the underlying Saxon implementation. However, GraalVM is
                highly optimised and can, in some cases, deliver faster execution times due to
                reduced startup overhead, the absence of JVM warm-up costs, and the ability to
                execute operations at near-native speed.</para>
            <para>Comparison of the execution times across the different benchmark scenarios reveals
                several interesting findings, which we summarise as follows: <orderedlist>
                    <listitem>
                        <para>
                            <emphasis>When is it advisable to use user extension functions?</emphasis> 
                            The <code>math:sqrt()</code> benchmark test is
                            a clear example where the use of user extension functions across all
                            host languages delivered significantly better performance than running
                            pure XSLT in the same environment. In the case of Java and C++, the
                            extension-function implementations achieved performance close to optimal
                            native execution speed. As a baseline running the <code>math:sqrt()</code>
                            in pure Java code that is without any XSLT, ran in 0.673
                            seconds. The pure XSLT implementation in
                            Java completed in approximately 9 seconds, whereas the version using an
                            extension function completed in just over 1 second, making it
                            approximately seven times faster.</para>


                        <para> We observed similar execution times for the C++ extension function,
                            which also completed in just over 1 second. In this benchmark, most of
                            the work was performed within the C++ function itself. By contrast, the
                            pure XSLT implementation executed through SaxonC C++ API ran in
                            approximately 71 seconds to completion. This indicates that, for this
                            CPU-intensive workload, pure XSLT execution in SaxonC was almost eight
                            times slower than pure XSLT execution in Java. In general, we observed
                            here the overhead associated with crossing the boundary between the
                            SaxonC APIs and the underlying implementation was approximately four
                            times greater than in Java, although this varied depending on the nature
                            and duration of the workload.</para>

                        <para>The SaxonC Python implementation showed similar times for the pure
                            XSLT execution to those observed in C++. The Python extension function
                            was completed in approximately 55 seconds. This is still faster than
                            pure XSLT executed in Python. But this raises the question of why the
                            square-root benchmark performs substantially faster in C++ than in
                            Python. The difference can largely be explained by the overhead of
                            repeated execution within the Python interpreter, including dynamic type
                            handling, function invocation, and object management. By contrast, the
                            C++ implementation is compiled directly to machine code, allowing the
                            computation to execute much more efficiently. </para>
                    </listitem>

                    <listitem>
                        <para>
                            <emphasis>Tree navigation is a clear winner for pure XSLT.</emphasis>
                            Across all languages, the tree-navigation benchmark using a predicate
                            showed that pure XSLT significantly outperformed the equivalent
                            extension-function implementation. In Saxon-EE, a query such as
                                <code>//x[a=42]</code> is optimized by building a document-level
                            index, meaning that if such a query is executed multiple times, all but
                            the first execution operate in constant time, independent of document
                            size. The user-written extension function does not attempt any similar
                            optimization. On Java and Python, pure XSLT performed better than user
                            extension functions: 2.5 times faster for Java and 15 times faster for
                            Python. The results for C++ were much closer between pure XSLT and C++
                            user function, which may be due to inefficiencies in the allocation and
                            deallocation of objects created during tree navigation. Each callback
                            also requires crossing the langauge boundary between the host-language
                            API and the underlying Saxon implementation in Java. </para>

                        <para> Another explanation is for Java and Python, the objects lifetime are
                            largely managed by the garbage collection, the additional
                            memory-management overhead in C++ may contribute to the observed
                            slowdown. The tree-walk benchmark involving string retrieval also showed
                            that pure XSLT execution in SaxonC was approximately four times slower
                            than the corresponding Java execution. Unlike the pure Java
                            implementation, SaxonC incurs additional overhead because string values
                            must be copied when crossing the boundary between the underlying Java
                            implementation and the C++ API layer. </para>
                    </listitem>

                    <listitem>
                        <para>
                            <emphasis>For simple operations pure XSLT versus extension functions
                                shows little advantage.</emphasis> The <code>abs()</code> benchmark
                            showed only a small difference between the pure XSLT implementation and
                            the extension-function implementation in Java. For such a simple
                            operation, the overhead of invoking an extension function outweighs most
                            of the potential benefits. The small difference can be concluded as
                            machine differences at the time of execution. In these cases, pure XSLT
                            is generally preferable because it avoids the additional complexity
                            associated with implementing and maintaining user-defined extension
                            functions. </para>
                    </listitem>


                </orderedlist>
            </para>
        </section>
    </section>



    <section>
        <title>Conclusion</title>

        <para>In summary, the results show that combining XSLT or XQuery with host-language
            user-defined extension functions can significantly improve performance. However, their
            use should be guided by the nature of the task being performed.</para>

        <para>Extension functions should be viewed as a powerful tool for addressing computationally
            intensive operations or tasks that are difficult to express efficiently in XSLT or
            XQuery alone. In some cases, the performance gains may arise from limitations or
            inefficiencies in the implementation of a particular XSLT or XQuery solution. Where
            there is a substantial performance gap, as demonstrated by the math square root
            benchmark and other computationally intensive or recursive operations, the use of a
            host-language extension function can provide dramatic improvements in execution
            time.</para>

        <para>By contrast, for XML-centric operations such as tree navigation and predicate
            evaluation, pure XSLT often remains the most efficient solution. This is particularly 
            true when the XPath optimizer finds an smart execution strategy, for example, construction 
            of an index as in this example.Developers should
            therefore consider extension functions selectively, using them where they provide a
            clear benefit while continuing to rely on the strengths of the XSLT and XQuery
            processing model for document-oriented tasks.</para>
    </section>

    <appendix xml:id="appendix-saxon-variants">
        <title>Overview of Saxon Products</title>

        <para>This appendix provides a brief overview of the Saxon product family to give context
            for the experiments presented in this paper. Saxon is an implementation of the W3C
            Recommendations for XSLT, XQuery, XPath, and XML Schema validation, and forms part of a
            wider ecosystem of XML technologies used for transformation, querying, validation, and
            document processing <citation>SAXON</citation>.</para>

        <para> Saxon is available in several product variants, each designed to support different
            deployment environments and application requirements. The user-defined extension
            function feature described in this paper is available in the commercial edition of
            Saxon, called Saxon-EE.</para>

        <section>
            <title>SaxonJ</title>

            <para>SaxonJ is the Java implementation of Saxon and is the reference implementation
                from which the other Saxon products are derived. It provides APIs through the s9api
                interface and supports execution of XSLT, XQuery, XPath, and XML Schema validation
                on the Java Virtual Machine (JVM). SaxonJ also provides the extension function
                framework used to implement user-defined functions in Java. </para>
        </section>

        <section>
            <title>SaxonC</title>

            <para>SaxonC is built from SaxonJ using GraalVM Native Image technology to produce
                native executables and libraries. It provides native APIs for C and C++, together
                with language bindings for Python and PHP. Beginning with SaxonC 13, applications
                can implement user-defined extension functions directly in C++, Python, and PHP,
                allowing XSLT, XQuery, and XPath expressions to invoke host-language code through
                the SaxonC APIs.</para>
        </section>

        <section>
            <title>SaxonJS</title>

            <para> SaxonJS is the JavaScript implementation of Saxon designed for execution within
                web browsers and JavaScript environments. It supports client-side execution of XSLT
                3.0 stylesheets and forms the execution engine for applications such as Saxon-Forms.
            </para>
        </section>

    </appendix>



    <bibliography>
        <title>References</title>

        <bibliomixed xml:id="XSLT30" xreflabel="XSLT30">Kay, Michael. <emphasis>XSL Transformations
                (XSLT) Version 3.0</emphasis>, 8 June 2017. W3C Recommendation. <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">http://www.w3.org/TR/xslt30</link>
        </bibliomixed>
        <bibliomixed xml:id="XQUERY31" xreflabel="XQUERY31">Robie, Jonathan, Dyck, Michael, and Spiegel, Josh. 
            <emphasis>XQuery 3.1: An XML Query Language</emphasis>, 21 March 2017. W3C Recommendation. <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://www.w3.org/TR/xquery-31</link>
        </bibliomixed>
        <bibliomixed xml:id="CYTHON" xreflabel="CYTHON"><emphasis>Cython</emphasis>.  
            <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://cython.readthedocs.io/</link>
        </bibliomixed>

        <bibliomixed xml:id="SAXONFORMS" xreflabel="SAXONFORMS">Delpratt, O'Neil and Lockett, Debbie. 
                <quote>Implementing XForms using interactive XSLT 3.0.</quote> Presented at XML
            Prague 2018. In <emphasis>Proceedings of XML Prague 2018</emphasis>: <link xlink:href="http://archive.xmlprague.cz/2018/files/xmlprague-2018-proceedings.pdf" xlink:type="simple" xlink:show="new" xlink:actuate="onRequest"/>
        </bibliomixed>

        <bibliomixed xml:id="SAXON" xreflabel="SAXON">SaxonJ and SaxonC Products.
                <emphasis>Saxonica</emphasis>. <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://www.saxonica.com/</link>
        </bibliomixed>

        <bibliomixed xml:id="SAXONJ-EX" xreflabel="SAXONJ-EX">SaxonJ - Java extension functions:
            simple interface. <emphasis>Saxonica</emphasis>. <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://www.saxonica.com/documentation13/index.html#!extensibility/extension-functions-J/ext-simple-J</link>
        </bibliomixed>

        <bibliomixed xml:id="SAXONC-EX" xreflabel="SAXONC-EX">SaxonC - C++ and Python Extension
            functions. <emphasis>Saxonica</emphasis>. <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://www.saxonica.com/saxon-c/documentation13/index.html#!extensibility</link>
        </bibliomixed>

        <bibliomixed xml:id="BASEX" xreflabel="BASEX">BaseX Java Bindings.
                <emphasis>BaseX</emphasis>. <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://docs.basex.org/main/Java_Bindings</link>
        </bibliomixed>

        <bibliomixed xml:id="GRAALVM" xreflabel="GRAALVM"><emphasis>GRaalVM</emphasis>. 
            <link xlink:type="simple" xlink:show="new" xlink:actuate="onRequest">https://www.Graalvm.org/</link>
        </bibliomixed>

    </bibliography>
</article>