yelp:cache implementation
- From: Brent Smith <gnome nextreality net>
- To: GNOME Documentation <gnome-doc-list gnome org>, Shaun McCance <shaunm gnome org>, Don Scorgie <DonScorgie Blueyonder co uk>
- Subject: yelp:cache implementation
- Date: Thu, 29 Jun 2006 20:36:28 -0600
This is the first rough pass at an implementation of a yelp:cache
extension element.
Shaun and I discussed this on IRC and basically the premise is that if a
template depends solely on a xsl:param element that is passed to it,
then we can cache the results of that template and if it is called again
with the same parameter, then we look up the result in a hash table
instead of performing all the XSLT again.
A prime example of this is the db.number template.
<xsl:template name="db.number">
<xsl:param name="node" select="."/>
<xsl:apply-templates mode="db.number.mode" select="$node"/>
</xsl:template>
This template is responsible for calculating the numbers for a
particular docbook section, and takes up quite a bit of processing time
for docbook files with many sections and deep nesting. Since the same
node is passed to the function often, a considerable amount of time is
wasted recalculating something we've already done before.
In comes the <yelp:cache> extension element. Yelp's db2html.xsl
template now has the following (overriding the original version of this
template in db-label.xsl)
<xsl:template name="db.number">
<xsl:param name="node" select="."/>
<yelp:cache key="db.number" node="$node">
<xsl:apply-templates mode="db.number.mode" select="$node"/>
</yelp:cache>
</xsl:template>
You can see that two attributes are required for the <yelp:cache>
element. The key is unique value per template and is used in generating
the key for the hash table lookup. The other required attribute is
node, which right now must be a nodeset with a single node. The
xmlNodePtr to this single node makes up the other part of the hash key.
On the first call to the db.number template with a particular node, the
extension element function will not find a value in the hash table.
Therefore, it will apply the child elements of the yelp:cache element,
and put the result in the hash table. On the second call with the same
node, the extension element function finds the value in the hash table,
and instantiates it in the result tree.
So enough blabbing, here are the results before and after for the
Gnumeric manual, which takes the longest time to process (by far).
BEFORE:
smitten home:/extra/cvs/gnome2/yelp-head2$ YELP_DEBUG="enable-profiling"
/opt/gnome2/bin/yelp
PROFILE [20:31:59]: entering xslt_pager_process
PROFILE [20:32:52]: leaving xslt_pager_process
AFTER:
smitten home:/extra/cvs/gnome2/yelp-head2$ YELP_DEBUG="enable-profiling"
/opt/gnome2/bin/yelp
PROFILE [20:30:10]: entering xslt_pager_process
PROFILE [20:30:35]: leaving xslt_pager_process
Pretty dramatic!!
Shaun had some concerns about localization, but I don't think this
should affect it since the extension element function caches the actual
result of the db.number.mode template. That is unless the result of
db.number.mode is dependent on the depth of numbering as well as the
$node parameter...
Let me know your thoughts!
--
Brent Smith <gnome nextreality net>
IRC: smitten
? data/info.xml
? m4/intltool.m4
? po/stamp-it
? src/client-bindings.h
? src/server-bindings.h
? src/stylesheet
Index: src/yelp-debug.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-debug.c,v
retrieving revision 1.2
diff -u -p -r1.2 yelp-debug.c
--- src/yelp-debug.c 12 Jun 2006 04:39:59 -0000 1.2
+++ src/yelp-debug.c 30 Jun 2006 02:12:19 -0000
@@ -23,6 +23,7 @@
#include <glib.h>
#include <glib/gprintf.h>
#include <unistd.h>
+#include <time.h>
#include "yelp-debug.h"
@@ -160,7 +161,16 @@ void yelp_debug (const gchar *file,
}
if (flags & DB_PROFILE) {
+ time_t t;
+ struct tm *tmp;
+ gchar timestamp[20];
+
+ t = time (NULL);
+ tmp = localtime(&t);
+
+ strftime (timestamp, 20, "%T", tmp);
formatted = g_strdup_vprintf (format, args);
+ g_fprintf (stdout, "PROFILE [%s]: %s\n", timestamp, formatted);
str = g_strdup_printf ("MARK: %s: %s", g_get_prgname(), formatted);
access (str, F_OK);
g_free (formatted);
Index: src/yelp-xslt-pager.c
===================================================================
RCS file: /cvs/gnome/yelp/src/yelp-xslt-pager.c,v
retrieving revision 1.17
diff -u -p -r1.17 yelp-xslt-pager.c
--- src/yelp-xslt-pager.c 12 Jun 2006 04:39:59 -0000 1.17
+++ src/yelp-xslt-pager.c 30 Jun 2006 02:12:20 -0000
@@ -162,6 +162,7 @@ xslt_pager_process (YelpPager *pager)
GError *error = NULL;
debug_print (DB_FUNCTION, "entering\n");
+ debug_print (DB_PROFILE, "entering %s", __FUNCTION__);
g_return_val_if_fail (pager != NULL, FALSE);
g_return_val_if_fail (YELP_IS_XSLT_PAGER (pager), FALSE);
@@ -276,6 +277,8 @@ xslt_pager_process (YelpPager *pager)
}
g_object_unref (pager);
+
+ debug_print (DB_PROFILE, "leaving %s", __FUNCTION__);
return FALSE;
}
@@ -459,9 +462,113 @@ xslt_yelp_cache (xsltTransformContextPtr
xmlNodePtr inst,
xsltStylePreCompPtr comp)
{
+ static GHashTable *keyhash; /* hash table of hash tables :-) */
+ xmlXPathObjectPtr nodeexpr;
+ xsltStylesheetPtr style = NULL;
+ xmlNodePtr nodeptr;
+ xmlChar *keyprop;
+ xmlChar *nodeprop;
+ const char *old_outfile;
+ xmlDocPtr old_output;
+ xmlNodePtr old_insert;
+ xmlDocPtr new_doc = NULL;
+ xmlChar *page_buf;
+ gint buf_size;
+ gchar *key;
+ xmlNodePtr tmpnode;
+ xmlNodePtr tmpnode2;
+
+ if (!ctxt || !node || !inst || !comp)
+ return;
+
+ keyprop = xmlGetProp (inst, BAD_CAST "key");
+ if (!keyprop)
+ return;
+
+ nodeprop = xmlGetProp (inst, BAD_CAST "node");
+ if (!nodeprop) {
+ xmlFree (keyprop);
+ return;
+ }
+
+ nodeexpr = xmlXPathEvalExpression (nodeprop, ctxt->xpathCtxt);
+ if (!nodeexpr)
+ goto done;
+
+ /* if we haven't initialize the hash yet, do so */
+ if (!keyhash)
+ keyhash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, xmlFree);
+
+ if (nodeexpr->type != XPATH_NODESET) {
+ debug_print (DB_WARN, "node attribute did not evaluate to a nodeset\n");
+ goto done;
+ }
+
+ nodeptr = nodeexpr->nodesetval->nodeTab[0];
+ /*g_print ("key=%s node=%s type=%d ptr=%p\n", (gchar *)keyprop, (gchar *)nodeprop, nodeexpr->type, nodeptr);*/
+
+ key = g_strdup_printf ("%s%p", keyprop, nodeptr);
+ tmpnode = g_hash_table_lookup (keyhash, key);
+
+ if (tmpnode) {
+/* g_print ("found cached result\n");*/
+ tmpnode2 = xmlDocCopyNode (tmpnode, tmpnode->doc, 1);
+ xmlAddChild (ctxt->insert, tmpnode2);
+ g_free (key);
+ goto done;
+ }
+
+ old_outfile = ctxt->outputFile;
+ old_output = ctxt->output;
+ old_insert = ctxt->insert;
+ ctxt->outputFile = "test";
+
+ style = xsltNewStylesheet ();
+ if (style == NULL) {
+ xsltTransformError (ctxt, NULL, inst, _("Out of memory"));
+ }
+
+ style->omitXmlDeclaration = TRUE;
+
+ new_doc = xmlNewDoc (BAD_CAST "1.0");
+ new_doc->charset = XML_CHAR_ENCODING_UTF8;
+ new_doc->dict = ctxt->dict;
+ xmlDictReference (new_doc->dict);
+
+ ctxt->output = new_doc;
+ ctxt->insert = (xmlNodePtr) new_doc;
+
xsltApplyOneTemplate (ctxt, node, inst->children, NULL, NULL);
+
+ /*xsltSaveResultToString (&page_buf, &buf_size, new_doc, style);
+
+ g_print ("page_buf = %s\n", page_buf);*/
+
+ ctxt->outputFile = old_outfile;
+ ctxt->output = old_output;
+ ctxt->insert = old_insert;
+
+ /*for (tmpnode = new_doc->children; tmpnode != NULL; tmpnode = tmpnode->next) {
+ g_print ("type=%d name=%s\n", tmpnode->type, tmpnode->name);
+ }*/
+
+ tmpnode = xmlCopyNode (new_doc->children, 1);
+ g_hash_table_insert (keyhash, key, tmpnode);
+
+ tmpnode2 = xmlDocCopyNode (tmpnode, tmpnode->doc, 1);
+ xmlAddChild (ctxt->insert, tmpnode2);
+
+ /*g_print ("cached result\n");*/
while (gtk_events_pending ())
gtk_main_iteration ();
/* FIXME : check for cancel */
+
+done:
+ xmlFree (keyprop);
+ xmlFree (nodeprop);
+ if (new_doc)
+ xmlFreeDoc (new_doc);
+ if (style)
+ xsltFreeStylesheet (style);
}
Index: stylesheets/db2html.xsl.in
===================================================================
RCS file: /cvs/gnome/yelp/stylesheets/db2html.xsl.in,v
retrieving revision 1.19
diff -u -p -r1.19 db2html.xsl.in
--- stylesheets/db2html.xsl.in 19 May 2005 22:41:16 -0000 1.19
+++ stylesheets/db2html.xsl.in 30 Jun 2006 02:12:20 -0000
@@ -58,6 +58,13 @@
<xsl:param name="db.chunk.index_basename" select="'__yelp_index'"/>
<xsl:param name="db.chunk.toc_basename" select="'__yelp_toc'"/>
+<xsl:template name="db.number">
+ <xsl:param name="node" select="."/>
+ <yelp:cache key="db.number" node="$node">
+ <xsl:apply-templates mode="db.number.mode" select="$node"/>
+ </yelp:cache>
+</xsl:template>
+
<!-- == db.chunk == -->
<xsl:template name="db.chunk">
<xsl:param name="node" select="."/>
[
Date Prev][Date Next] [
Thread Prev][Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]