dbertoni 00/07/25 08:12:10
Modified: c/src/XSLT StylesheetExecutionContextDefault.cpp
StylesheetExecutionContextDefault.hpp
Log:
Added caching if match patterns created at runtime.
Revision Changes Path
1.23 +199 -4
xml-xalan/c/src/XSLT/StylesheetExecutionContextDefault.cpp
Index: StylesheetExecutionContextDefault.cpp
===================================================================
RCS file:
/home/cvs/xml-xalan/c/src/XSLT/StylesheetExecutionContextDefault.cpp,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- StylesheetExecutionContextDefault.cpp 2000/07/21 19:52:57 1.22
+++ StylesheetExecutionContextDefault.cpp 2000/07/25 15:12:06 1.23
@@ -141,7 +141,8 @@
m_textOutputStreams(),
m_collationCompareFunctor(&s_defaultFunctor),
m_liveVariablesStack(),
- m_variablesStack()
+ m_variablesStack(),
+ m_matchPatternCache()
{
m_liveVariablesStack.reserve(eDefaultVariablesStackSize);
}
@@ -183,6 +184,8 @@
clearLiveVariablesStack();
m_variablesStack.reset();
+
+ assert(m_matchPatternCache.size() == 0);
}
@@ -454,7 +457,44 @@
const XalanDOMString& str,
const PrefixResolver& resolver)
{
- return m_xsltProcessor.createMatchPattern(str, resolver);
+ XPath* theResult = 0;
+
+ // We won't cache any xpath that has a namespace, since
+ // we have no idea how that might be resolved. We could
+ // enhance XPath so that we can tell if str would match
+ // the XPath, once the namespace is resolved, but it may
+ // not be worth it...
+ const unsigned int index = indexOf(str, ':');
+ const unsigned int len = length(str);
+
+ // If we found a ':' before the end of the string, and
+ // it's by itself (:: would indicate an axis), don't
+ // try to cache the XPath...
+ if (index < len - 1 && (charAt(str, index + 1) != ':'))
+ {
+ theResult = m_xsltProcessor.createMatchPattern(str, resolver);
+ }
+ else
+ {
+ const XPathCacheMapType::iterator i =
+ m_matchPatternCache.find(str);
+
+ if (i != m_matchPatternCache.end())
+ {
+ // Update hit time...
+ i->second.second = clock();
+
+ theResult = i->second.first;
+ }
+ else
+ {
+ theResult = m_xsltProcessor.createMatchPattern(str,
resolver);
+
+ addToXPathCache(str, theResult);
+ }
+ }
+
+ return theResult;
}
@@ -462,7 +502,10 @@
void
StylesheetExecutionContextDefault::returnXPath(XPath* xpath)
{
- m_xsltProcessor.returnXPath(xpath);
+ if (isCached(xpath) == false)
+ {
+ m_xsltProcessor.returnXPath(xpath);
+ }
}
@@ -735,7 +778,7 @@
mode);
}
-
theParams.push_back(ParamsVectorType::value_type(xslParamElement->getQName(),
theXObject));
+
theParams.push_back(ParamsVectorType::value_type(&xslParamElement->getQName(),
theXObject));
}
child = child->getNextSiblingElem();
@@ -808,6 +851,9 @@
// This matches the pushContextMarker in
// resolveTopLevelParams().
popContextMarker();
+
+ // Clear any cached XPaths...
+ clearXPathCache();
}
@@ -1774,3 +1820,152 @@
}
}
+
+
+bool
+StylesheetExecutionContextDefault::isCached(const XPath* theXPath)
+{
+ XPathCacheMapType::const_iterator i =
+ m_matchPatternCache.begin();
+
+ const XPathCacheMapType::const_iterator theEnd =
+ m_matchPatternCache.end();
+
+ while(i != theEnd)
+ {
+ if (i->second.first == theXPath)
+ {
+ return true;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ return false;
+}
+
+
+
+// I should be able to make this out of a
+// bunch of compose<> and select2nd<> adapters...
+template<class Type>
+class XPathCacheReturnFunctor
+{
+public:
+
+ XPathCacheReturnFunctor(XSLTEngineImpl& xsltProcessor) :
+ m_xsltProcessor(xsltProcessor)
+ {
+ }
+
+ void
+ operator()(const Type& theCacheEntry)
+ {
+ m_xsltProcessor.returnXPath(theCacheEntry.second.first);
+ }
+
+private:
+
+ XSLTEngineImpl& m_xsltProcessor;
+};
+
+
+
+void
+StylesheetExecutionContextDefault::clearXPathCache()
+{
+#if !defined(XALAN_NO_NAMESPACES)
+ using std::for_each;
+#endif
+
+ for_each(m_matchPatternCache.begin(),
+ m_matchPatternCache.end(),
+
XPathCacheReturnFunctor<XPathCacheMapType::value_type>(m_xsltProcessor));
+
+ m_matchPatternCache.clear();
+}
+
+
+
+// I should be able to make this out of a
+// bunch of compose<> and select2nd<> adapters...
+template<class Type>
+class XPathCacheEarliestPredicate
+{
+public:
+
+ XPathCacheEarliestPredicate(XSLTEngineImpl& xsltProcessor) :
+ m_xsltProcessor(xsltProcessor)
+ {
+ }
+
+ void
+ operator()(const Type& theCacheEntry)
+ {
+ m_xsltProcessor.returnXPath(theCacheEntry.second.first);
+ }
+
+private:
+
+ XSLTEngineImpl& m_xsltProcessor;
+};
+
+
+
+
+
+
+void
+StylesheetExecutionContextDefault::addToXPathCache(
+ const XalanDOMString& pattern,
+ XPath* theXPath)
+{
+ clock_t addClock = clock();
+
+ if (m_matchPatternCache.size() == eXPathCacheMax)
+ {
+ // OK, we need to clear something out of the cache...
+
+ // Initialize the lowest clock time found so far
+ // with the current clock...
+ clock_t lowest = addClock;
+
+ // Get some iterators ready to search the cache...
+ XPathCacheMapType::iterator i =
+ m_matchPatternCache.begin();
+
+ const XPathCacheMapType::iterator theEnd =
+ m_matchPatternCache.end();
+
+ XPathCacheMapType::iterator earliest(theEnd);
+
+ while(i != theEnd)
+ {
+ const clock_t current = i->second.second;
+
+ if (current < lowest)
+ {
+ // OK, found a lower clock time, so
+ // update the everything...
+ lowest = current;
+
+ earliest = i;
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ assert(earliest != theEnd);
+
+ // Return the XPath and erase it from the cache.
+ m_xsltProcessor.returnXPath(earliest->second.first);
+
+ m_matchPatternCache.erase(earliest);
+ }
+
+ // Add the XPath with the current clock
+ m_matchPatternCache.insert(XPathCacheMapType::value_type(pattern,
XPathCacheEntry(theXPath, addClock)));
+}
1.21 +45 -3
xml-xalan/c/src/XSLT/StylesheetExecutionContextDefault.hpp
Index: StylesheetExecutionContextDefault.hpp
===================================================================
RCS file:
/home/cvs/xml-xalan/c/src/XSLT/StylesheetExecutionContextDefault.hpp,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- StylesheetExecutionContextDefault.hpp 2000/07/21 19:52:57 1.20
+++ StylesheetExecutionContextDefault.hpp 2000/07/25 15:12:07 1.21
@@ -66,6 +66,7 @@
+#include <ctime>
#include <deque>
#include <memory>
#include <set>
@@ -457,11 +458,10 @@
* @param a pointer to the new factory instance to use.
* @return a pointer to the old factory instance.
*/
- //
static XalanNumberFormatFactory*
installXalanNumberFormatFactory(XalanNumberFormatFactory*
theFactory);
- // Trace interface...
+
virtual unsigned long
getTraceListeners() const;
@@ -713,12 +713,46 @@
private:
+ /**
+ * Pop the top entry from the live variables.
+ */
void
popLiveVariablesStack();
+ /**
+ * Clear out the entire stack of live variables.
+ */
void
clearLiveVariablesStack();
+ /**
+ * Determine if the XPath is one that we have cached.
+ *
+ * @param theXPath the XPath instance to check
+ * @return true if the instance has been cached, false if not.
+ */
+ bool
+ isCached(const XPath* theXPath);
+
+ /**
+ * Clear out the cache of XPath instances.
+ */
+ void
+ clearXPathCache();
+
+ /**
+ * Add an XPath instance to the cache, clearing out an old entry
+ * if the cache is full.
+ *
+ * @param pattern the key for looking up the XPath instance in the
cache.
+ * @param theXPath the XPath instance to cache
+ */
+ void
+ addToXPathCache(
+ const XalanDOMString& pattern,
+ XPath* theXPath);
+
+
XPathExecutionContextDefault m_xpathExecutionContextDefault;
// $$ ToDo: Try to remove this dependency, and rely only on
XSLTProcessor...
@@ -733,6 +767,8 @@
typedef set<TextOutputStream*>
TextOutputStreamSetType;
typedef vector<const XObject*>
VariablesCollectionType;
typedef vector<VariablesCollectionType>
LiveVariablesStackType;
+ typedef pair<XPath*, clock_t>
XPathCacheEntry;
+ typedef map<XalanDOMString, XPathCacheEntry>
XPathCacheMapType;
#else
typedef std::deque<const ElemTemplateElement*>
ElementRecursionStackType;
typedef std::set<FormatterListener*>
FormatterListenerSetType;
@@ -740,9 +776,13 @@
typedef std::set<TextOutputStream*>
TextOutputStreamSetType;
typedef std::vector<const XObject*>
VariablesCollectionType;
typedef std::vector<VariablesCollectionType>
LiveVariablesStackType;
+ typedef std::pair<XPath*, clock_t>
XPathCacheEntry;
+ typedef std::map<XalanDOMString, XPathCacheEntry>
XPathCacheMapType;
#endif
- enum { eDefaultVariablesCollectionSize = 10, eDefaultVariablesStackSize
= 200 };
+ enum { eDefaultVariablesCollectionSize = 10,
+ eXPathCacheMax = 50,
+ eDefaultVariablesStackSize = 200 };
ElementRecursionStackType m_elementRecursionStack;
@@ -764,6 +804,8 @@
* Holds all information about variables during execution.
*/
VariablesStack
m_variablesStack;
+
+ XPathCacheMapType
m_matchPatternCache;
static XalanNumberFormatFactory
s_defaultXalanNumberFormatFactory;