2012-03-10 9 views
2

私のC#アプリケーションは、次の構造のXMLファイルを読み込みます。 150MBファイルには約25万語があります。 に私が欲しい辞書にXML読み込みが遅くなる

<word> 
    <name>kick</name> 
    <id>485</id> 
    <rels>12:4;4256:3;754:3;1452:2;86:2;125:2;</rels> 
</word> 

辞書にXML-ファイルをお読みください。これらは私の読書クラスのいくつかのクラスメンバーです。

private XmlReader Reader; 

public string CurrentWordName; 
public int CurrentWordId; 
public Dictionary<KeyValuePair<int, int>, int> CurrentRelations; 

私の読書クラスの主な方法は次のとおりです。ファイルから次の単語を読み込み、name,idを取得し、その関係を辞書に格納します。

CurrentWordId = -1; 
CurrentWordName = ""; 
CurrentRelations = new Dictionary<KeyValuePair<int, int>, int>(); 

while(Reader.Read()) 
    if(Reader.NodeType == XmlNodeType.Element & Reader.Name == "word") 
    { 
     while (Reader.Read()) 
      if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "name") 
      { 
       XElement Title = XElement.ReadFrom(Reader) as XElement; 
       CurrentWordName = Title.Value; 
       break; 
      } 
     while (Reader.Read()) 
      if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "id") 
      { 
       XElement Identifier = XElement.ReadFrom(Reader) as XElement; 
       CurrentWordId = Convert.ToInt32(Identifier.Value); 
       break; 
      } 
     while(Reader.Read()) 
      if (Reader.NodeType == XmlNodeType.Element & Reader.Name == "rels") 
      { 
       XElement Text = XElement.ReadFrom(Reader) as XElement; 
       string[] RelationStrings = Text.Value.Split(';'); 
       foreach (string RelationString in RelationStrings) 
       { 
        string[] RelationsStringSplit = RelationString.Split(':'); 
        if (RelationsStringSplit.Length == 2) 
         CurrentRelations.Add(new KeyValuePair<int,int>(CurrentWordId,Convert.ToInt32(RelationsStringSplit[0])), Convert.ToInt32(RelationsStringSplit[1])); 
       } 
       break; 
      } 
     break; 
    } 

if (CurrentRelations.Count < 1 || CurrentWordId == -1 || CurrentWordName == "") 
    return false; 
else 
    return true; 

私のWindowsフォームは、すべての単語を読むことbackgroundWorkerを持っています。私をデバッグすることで

private void bgReader_DoWork(object sender, DoWorkEventArgs e) 
{ 
    ReadXML Reader = new ReadXML(tBOpenFile.Text); 

    Words = new Dictionary<int, string>(); 
    Dictionary<KeyValuePair<int, int>, int> ReadedRelations = new Dictionary<KeyValuePair<int, int>, int>(); 

    // reading 
    while(Reader.ReadNextWord()) 
    { 
     Words.Add(Reader.CurrentWordId, Reader.CurrentWordName); 

     foreach (KeyValuePair<KeyValuePair<int, int>, int> CurrentRelation in Reader.CurrentRelations) 
     { 
      ReadedRelations.Add(new KeyValuePair<int, int>(CurrentRelation.Key.Key, CurrentRelation.Key.Value), CurrentRelation.Value); 
     } 
    } 

アプリケーションが本当に高速で開始し、時間で遅くなることに気づきました。最初20万の言葉私は、この動作を説明することはできません最初22万言葉

ため

  • 35分間の最初の10,000語
  • 30分間

    • 7秒!しかし、私は、XMLファイル内の単語が平均で同じサイズであることを確信しています。多分Add() - 方法は辞書の長さによって遅くなります。

      アプリケーションを高速化するにはどうすればよいですか?

  • +0

    代わりにノードによってノードを読み取るXML – Lloyd

    +0

    へのLINQを見て多分それは '辞書<持つように助けることができますint、Dictionary > 'を使用し、ダブルキーを使用して1回ではなく2回インデックスを作成します。データによって異なります。 – harold

    +1

    @Lloyd、どのようにパフォーマンスを向上させるのに役立ちますか? – svick

    答えて

    3

    EDIT:さて、今私は、コードを実行したことを、私はこれが問題であると考えている。そのループなし

    foreach (KeyValuePair<KeyValuePair<int, int>, int> CurrentRelation in 
         Reader.CurrentRelations) 
    { 
        ReadedRelations.Add(new KeyValuePair<int, int>(CurrentRelation.Key.Key, 
         CurrentRelation.Key.Value), CurrentRelation.Value); 
    } 
    

    は、それは疑うために私をリードしている... ずっと速く動作しますあなたがXMLから読み込んでいるという事実は事実上赤ちゃんだということです。

    KeyValuePair<,>は、EqualsGetHashCodeを上書きしないという問題があると思われます。私はあなたがの値のタイプを2つのintの値を含んで作成し、GetHashCodeEquals(そしてIEquatable<RelationKey>を実装する)を上書きすると、それはずっと速くなると思います。

    また、の2つの値を保存することもできます。ちょっとしたハックですが、うまくいくはずです。私は今これをテストすることはできませんが、私は時間があればそれを行っていきます。

    でもちょうどにあなたのループを変更:

    foreach (var relation in Reader.CurrentRelations) 
    { 
        ReadedRelations.Add(relation.Key, relation.Value); 
    } 
    

    は簡単で、もう少し効率的である...

    編集:ここではRelationKey構造体のサンプルがあります。単にRelationKeyKeyValuePair<int, int>のすべての出現を交換し、代わりにKeyValueSourceTargetプロパティを使用します。

    public struct RelationKey : IEquatable<RelationKey> 
    { 
        private readonly int source; 
        private readonly int target; 
    
        public int Source { get { return source; } } 
        public int Target { get { return target; } } 
    
        public RelationKey(int source, int target) 
        { 
         this.source = source; 
         this.target = target; 
        } 
    
        public override bool Equals(object obj) 
        { 
         if (!(obj is RelationKey)) 
         { 
          return false; 
         } 
         return Equals((RelationKey)obj); 
        } 
    
        public override int GetHashCode() 
        { 
         return source * 31 + target; 
        } 
    
        public bool Equals(RelationKey other) 
        { 
         return source == other.source && target == other.target; 
        } 
    } 
    
    +0

    私はすでに 'BackgroundWorker'で動作していると思います。そのため、メソッドは' bgReader_DoWork'と呼ばれています。 – svick

    +0

    @スウィック:Doh - 前にそれを見逃したかどうかわかりません。編集中... –

    +0

    @JonSkeet:まず、「CurrentRelations」をクリアするのを忘れてしまいました。その後、私はこの質問を投稿しました。申し訳ありませんが、私はその重要な部分を忘れました。私は今質問コードを更新しました。 – danijar

    関連する問題