遅い - java xpath 使い方




XPath.evaluateのパフォーマンスが複数の呼び出しで低下する(不条理に) (4)

Nodelistからノードを取得するたびに、XMLの全体構造への参照が保持されているようです。 この理由から、ノードをナビゲートすると、xpathのプロセスはxmlのルートから毎回開始されます。このため、trheeを使用すると時間がかかります。

このような理由から、ノードを移動する前に、このメソッドで文字列をキャストする必要があります。

private String nodeToString(Node node) {
          StringWriter sw = new StringWriter();
          try {
            Transformer t = TransformerFactory.newInstance().newTransformer();
            t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            t.transform(new DOMSource(node), new StreamResult(sw));
          } catch (TransformerException te) {
            System.out.println("nodeToString Transformer Exception");
          }
          return sw.toString();
        }

要素/ノードでそれを再変換します。

String xml = nodeToString(node);

Element nodeNew =  DocumentBuilderFactory
        .newInstance()
        .newDocumentBuilder()
        .parse(new ByteArrayInputStream(xml.getBytes()))
        .getDocumentElement();

node = nodeNew;

このようにして、新しい要素は、祖先への参照をすべて失い、単純なノードとして使用され、入れ子のノードとしては使用されません。 明らかに、このメソッドは、ノードに深く移動する必要がある場合にのみ有効です。

私は、複数の名前空間を持つドキュメント上でXPath式を実行するためにjavax.xml.xpathパッケージを使用しようとしています。私はパフォーマンス上の問題を抱えています。

私のテスト文書は実際の生産例から引き出されています。 それはxmlの約600kです。 この文書は非常に複雑なAtomフィードです。

私は、XPathでやっていることがなくても済むことを理解しています。 しかし、他の非常に劣ったプラットフォーム上での同じ実装は、ばかげて良い結果を出します。 今、XPathを使用しないように私のシステムを再構築することは、私の時間にできることの範囲を超えています。

私のテストコードは次のようなものです:



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

     }
}

私がNexus One(デバッガではなく、USBを接続した状態)で実行すると、最初のループで、各xp.evaluateは10msから20msのどこかに移動します。 ループを通る15回目までに、各xp.evaluateは200msから300msのどこかにかかります。 ループの終わり( nodesには150の項目があります)では、各xp.evaluateに約500ms-600msかかります。

私はxp.compile()を使って試しました。 コンパイルはすべて<5msです。 私はxp.reset()を行った(違いはない)。 私は、各評価のために新しいXPathオブジェクトを作成しました(約4ms追加します)。

実行中にメモリ使用量が制御不能になっているように見えることはありません。

私は、JUnitのテストケース内でアクティビティなどを作成しない単一のスレッドでこれを実行しています。

私は本当に困惑しています。

他に何をしようと考えている人はいますか?

ありがとう!

更新

forループを逆方向に実行すると(最初のいくつかのノードは500ms-600msをとり、最後のノードは10ms高速になります) -20ms。 だから、これは呼び出し回数とは関係ないようですが、文脈が文書の終わり近くにある式は、文脈が文書の先頭近くにある式よりも時間がかかります。

誰も私がこれについて何をすることができるかについての考えを持っていますか?


このコードを最上部のループ内に追加してみてください。

Node singleNode = nodes.item(i);
singleNode.getParentNode().removeChild(singleNode);

nodes.item(i);ではなくsingleNode変数を使用して各評価を実行しnodes.item(i); (もちろんあなたは名前を変える)

これにより、作業中のノードが大きなメイン文書から切り離されます。 これにより、評価メソッドの処理時間が大幅に短縮されます。

EX:

for(int i=0;i<nodes.getLength();i++)
{
    Node singleNode = nodes.item(i);
    singleNode.getParentNode().removeChild(singleNode);

    printTimestamp(1);
    xp.evaluate("atom:id/text()", singleNode );
    printTimestamp(2);
    xp.evaluate("samplens:fieldA/text()", singleNode );
    printTimestamp(3);
    xp.evaluate("atom:author/atom:uri/text()", singleNode );
    printTimestamp(4);
    xp.evaluate("samplens:fieldA/samplens:fieldB/&at;attrC", singleNode );
    printTimestamp(5);

    //etc.  My real example has 10 of these xp.evaluate lines

 }

これはもう少し遅いですが、私は同じ状況に遭遇しましたが、私の文書が非常に大きく、他の回答のどれもが本当に問題を解決していないようでした。

結局、私はジャクセンを見つけました 。 いったんそれを使用すると、これまでパースするのに15秒かかっていた文書は数ミリ秒しかかかりませんでした。

残念ながら、Jaxenはかなり悪いことに文書化されていますが、かなりうまく機能しました。

DOMXPath myXPath = new DOMXPath("atom:id/text()");
String myContent = myXPath.stringValueOf(myDocument);

Java Docはhttp://jaxen.codehaus.org/apidocs/org/jaxen/dom/DOMXPath.html


ノードをクローンしてみてください(その祖先からの不要な参照はありません)

Node singleNode = nodes.item(i).cloneNode(true);

子を削除すると、参照を失い、処理したいノードの半分しか取得しません。





xpath