随着时间的推移,XML阅读到字典会变慢

时间:2012-03-10 10:03:51

标签: c# xml performance dictionary

我的 C#应用程序读取以下结构的XML-File 。 150mb文件中有大约250,000个单词。

<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;

这是我阅读课的主要方法。它只读取文件中的下一个单词并获取nameid,并将关系存储到字典中。

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);
        }
    }

通过调试,我注意到应用程序启动非常快,并且随着时间的推移而变慢

  • 前10,000个单词的7秒
  • 前20万字30分钟
  • 前220,000字35分钟

我无法解释这种行为!但我确信XML文件中的单词平均大小相同。也许Add() - 方法因字典长度变慢。

如何加快申请速度?

1 个答案:

答案 0 :(得分:3)

编辑:好的,既然我已经运行了代码,我相信这就是问题:

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。我相信,如果您创建自己的RelationKey值类型,其中包含两个int值并覆盖GetHashCodeEquals(以及实施IEquatable<RelationKey>),那么它将是快得多。

或者,您可以始终使用long来存储两个int值 - 这有点像黑客,但它会很好用。我现在无法测试,但是当我有更多时间时,我会试一试。

即使只是将你的循环改为:

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

会更简单,效率更高......

编辑:这是RelationKey结构的示例。只需将所有KeyValuePair<int, int>替换为RelationKey,并使用SourceTarget属性代替KeyValue

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;
    }
}