Próbuję użyć pakietu javax.xml.xpath do uruchamiania wyrażeń XPath w dokumencie z wieloma przestrzeniami nazw i mam niemądre problemy z wydajnością.XPath. Ewaluuj wydajność spowalnia (absurdalnie) przy wielu połączeniach
Mój dokument testowy jest wyciągany z prawdziwego, przykładowego przykładu. To około 600k xml. Dokument jest dość złożonym kanałem Atom.
Zdaję sobie sprawę, że to, co robię z XPath, można zrobić bez. Jednak ta sama implementacja na innych, znacznie gorszych platformach działa absurdalnie lepiej. Teraz, odbudowanie mojego systemu, aby nie używać XPath, wykracza poza zakres moich działań w tym czasie, jaki mam.
Mój kod test jest coś takiego:
void testXPathPerformance()
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(loadTestDocument());
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
NamespaceContext names = loadTestNamespaces();
//there are 12 namespaces in names. In this example code, I'm using
//'samplens' instead of the actual namespaces that my application uses
//for simplicity. In my real code, the queries are different text, but
//precisely the same complexity.
xp.setNamespaceContext(names);
NodeList nodes = (NodeList) xp.evaluate("/atom:feed/atom:entry",
doc.getDocumentElement(), XPathConstants.NODESET);
for(int i=0;i<nodes.getLength();i++)
{
printTimestamp(1);
xp.evaluate("atom:id/text()", nodes.item(i));
printTimestamp(2);
xp.evaluate("samplens:fieldA/text()", nodes.item(i));
printTimestamp(3);
xp.evaluate("atom:author/atom:uri/text()", nodes.item(i));
printTimestamp(4);
xp.evaluate("samplens:fieldA/samplens:fieldB/&at;attrC", nodes.item(i));
printTimestamp(5);
//etc. My real example has 10 of these xp.evaluate lines
}
}
Kiedy biegnę na Nexus One, (nie w debugger, ale z podłącz.USB), po raz pierwszy za pomocą pętli, każda xp.evaluate trwa gdzieś od 10ms do 20ms. Po 15 raz w pętli, każda ocena xp.e zatrudnia gdzieś od 200ms do 300ms. Pod koniec pętli (150 pozycji w nodes
) trwa około 500ms-600ms dla każdego xp.evaluate.
Próbowałem już używać xp.compile(). Wszystkie kompilacje zabierają < 5 ms. Zrobiłem xp.reset() (nie robi różnicy). Zrobiłem nowy obiekt XPath dla każdej oceny (dodaje około 4ms).
Wydaje się, że użycie pamięci nie ma wpływu na kontrolę podczas wykonywania.
Uruchomiłem to na pojedynczym wątku w teście JUnit, który nie tworzy żadnego działania.
Jestem naprawdę zaintrygowany.
Czy ktoś ma pojęcie, co jeszcze można wypróbować?
Dzięki!
aktualizacja
Jeśli biegnę do pętli wstecznej (for(int i=nodes.getLength()-1;i>=0;i--)
), następnie kilka pierwszych węzłów podejmuje 500ms-600ms, a te ostatnie iść szybko 10ms-20ms. Wydaje się więc, że nie ma to nic wspólnego z liczbą wywołań, ale zamiast tego wyrażenia, których kontekst znajduje się blisko końca dokumentu, trwają dłużej niż wyrażenia, których kontekst znajduje się blisko początku dokumentu.
Czy ktoś ma jakiekolwiek przemyślenia na temat tego, co mogę z tym zrobić?
@Andrew Shelansky: Czy próbowałeś uruchomić tylko jedno zapytanie używając '|' oporu zbioru węzłów? Zestaw węzłów wyników będzie w porządku dokumentu. –
@Andrew Shelansky: Domyślam się, że wartość NodeList zwracana przez wyrażenie XPath jest oceniana leniwie. Tak więc za każdym razem, gdy robisz nodes.item (i), musisz liczyć przez i przedmioty, aby znaleźć węzeł. Spróbuj zapisać węzeł w zmiennej na początku pętli i sprawdź, czy to pomaga. –
@Nick Jones. W moim kodzie testowym robię leniwy eval dla nodes.item (i). W moim kodzie produkcyjnym, właśnie iteruję przez węzły natychmiast po wywołaniu pierwszego xp.evaluate. Wynikowe węzły są przechowywane w mapie mieszającej od UUID do węzła i oceniane w ten sposób. Kod produkcyjny wykazuje ten sam problem. Dobra myśl. –