【问题标题】:Optimize large dictionary for reading in .NET [closed]优化在.NET中阅读的大字典[关闭]
【发布时间】:2016-09-04 09:07:10
【问题描述】:

我有一个数字字符串相关的情侣列表,其中有 22k。 (这是一个 MAC 地址供应商列表)。

在我的代码中,我通过 MAC 地址的前三个字节搜索供应商名称。

我知道,我可以使用字典,甚至可以使用数组,但是每次运行程序时都需要初始化字典,但程序只使用少量翻译(不到百分之一的项目在字典中)并且在程序运行时初始化字典需要大量时间。

你能想出其他方法吗?在旧的 VB6 中,可以读取二进制文件并查找记录,这对我来说已经足够了,因为我只会加载我实际需要的值。

我更喜欢项目内解决方案 - 因此没有包含数据的外部文件。我正在尝试使用如下代码:-

Vendors.add("00125A","Microsoft Corporation") 
'... this in another 22000 times '
Vendors.add("00124E","XAC AUTOMATION CORP.")

【问题讨论】:

  • 在后台线程中读取字典,如果您在程序启动后立即不需要它
  • 是控制台应用程序,所以我可以在另一个线程中加载它,但在字典加载完成之前我无法启动其他操作。
  • 在旧的 VB6 中可以读取二进制文件并查找记录 -> 在.Net 中你也可以读取二进制文件
  • 如果只读取 22k 数据需要很长时间,可能是字典初始化有问题。如果您向我们展示了代码,那么也许我们可以帮助您修复它。
  • 22k 并不是很多数据。我不知道“大量时间”是什么,但也许如果你展示代码有人可以提供一些优化想法

标签: c# .net vb.net dictionary


【解决方案1】:

不确定对你来说最好的行动方案是什么,或者这是否真的对你有帮助,但是..

您似乎正在寻找一种从结构化文件中查找和读取特定记录的方法。

为此,您可以定义一个封装记录字段以及访问方法的类。

这是一个例子。在我的机器上,它创建、存储 22k+ 条记录并在大约 20 毫秒内查找一些记录。 Otoh 进行 100 次随机搜索需要 3.5 秒,这显然是因为它总是从开头开始。再次进行顺序搜索相当快..

当然,总时间取决于您的机器以及您要查找和阅读的记录数..

这是一个记录类,它包含一个字节、一个长整数和一个字符串:

class aRecord
{
    byte aByte { get; set; }
    long aLong { get; set; }
    string aString { get; set; }

    public aRecord() { }

    public aRecord(byte b_, long l_, string s_)
    { aByte = b_; aLong = l_; aString = s_; }

    public void writeToStream(BinaryWriter bw )
    {
        bw.Write(aByte);
        bw.Write(aLong);
        bw.Write(aString);
    }

    public void readFromStream(BinaryReader br)
    {
        aByte = br.ReadByte();
        aLong = br.ReadInt64();
        aString = br.ReadString();
    }

    static public aRecord readFromStream(BinaryReader br, int record)
    {
        int r = 0;
        aRecord  rec = new aRecord();
        br.BaseStream.Position = 0;
        while (br.PeekChar() != -1 & r <= record  )
        {
            rec.readFromStream(br);
            r++;
        }
        return rec;
    }

    static public aRecord readFromStream(BinaryReader br, string search)
    {
        aRecord rec = new aRecord();
        while (br.PeekChar() != -1 )
        {
            rec.readFromStream(br);
            if (rec.aString.Contains(search)) return rec;
        }
        return null;
    }

}

我是这样测试的:

Console.WriteLine(DateTime.Now.ToString("ss,ffff") + "  init ");

List<aRecord> data = new List<aRecord>();

Random rnd = new Random(9);

int count = 23000;
for (int i = 1000; i < count; i++ )
{
    data.Add(new aRecord((byte)(i%128), i, "X" + rnd.Next(13456).ToString()));
}

Console.WriteLine(DateTime.Now.ToString("ss,ffff") + "  write ");

string fileName = "D:\\_DataStream.dat";

FileStream sw = new FileStream(fileName, FileMode.Create);
BinaryWriter bw = new BinaryWriter(sw);

foreach(aRecord r in data)
{
    r.writeToStream(bw);

}
bw.Flush();
sw.Close();
bw.Close();

FileStream sr = new FileStream(fileName, FileMode.Open);
BinaryReader br = new BinaryReader(sr);

List<aRecord> data2 = new List<aRecord>();
Console.WriteLine(DateTime.Now.ToString("ss,ffff") + "  begin search");
for (int i = 0; i < 100; i++)
{
    aRecord  rec = aRecord.readFromStream(br, "911");
    if (rec != null) data2.Add(rec);
}
Console.WriteLine(DateTime.Now.ToString("ss,ffff") + "  done. found " + data2.Count);


Console.WriteLine(DateTime.Now.ToString("ss,ffff") + "  seek ");

aRecord ar = aRecord.readFromStream(br, 0);
Console.WriteLine(DateTime.Now.ToString("ss,ffff") + " 0 ");

aRecord ar1 = aRecord.readFromStream(br, 1);
Console.WriteLine(DateTime.Now.ToString("ss,ffff") + " 1 ");

aRecord ar2 = aRecord.readFromStream(br, 13000);
Console.WriteLine(DateTime.Now.ToString("ss,ffff") + " 13000 ");

aRecord ar3 = aRecord.readFromStream(br, 23000-1);
Console.WriteLine(DateTime.Now.ToString("ss,ffff") + " 23000 end ");

br.Close();
sr.Close();

您的标题与优化Dictionary有关。这取决于主要用途:阅读还是写作?如果您在字典中阅读了很多内容,最好创建一个SortedDictionary。如果您需要创建比预期读取正常Dictionary 更多的 mor 条目会更好..

..还有更多的集合类,但首先要找出真正的瓶颈。上面的 seek&read 例程不会浪费时间将数据插入到 Dictionary 中,而是简单地丢弃它们,直到找到正确的记录。我还添加了一种搜索方法,该方法在每次点击同一位置后继续。扩展类以满足您自己的需要相当简单。

27,2208 初始化

27,2297 写

27,2438 次搜索

27,2438 开始搜索

27,3097 完成。找到 38 个

27,3097 0 结束

27,3097 1 个结束

27,3457 13000 结束

27,4037 23000 结束

【讨论】:

  • 多次通过二进制文件真的比将所有内容加载到字典中更快吗?在如此大的文件中进行少量查询是的 - 所以感谢您的灵感!
  • 这真的取决于您的使用情况!搜索总是通过 n 条记录,并且适合直接访问,而不是读取范围。搜索只是单通道访问的一个简单示例。 - 如果您知道要查找的记录和/或它们的顺序,您可以编写定制的访问方法,这可能不需要多次传递。您可以输入搜索字符串列表或数字范围等等。
  • 请注意,搜索示例遍历所有记录,并在短短 50 毫秒内找到并返回了 39 条记录。 - 你的用途是什么?或许我可以提供更多灵感?
【解决方案2】:

您可以将数据作为资源嵌入,然后使用ResourceManager 的实例来检索值。

var rm = new ResourceManager(baseName, assembly);
string vendor = rm.GetString(macAddress);

要创建资源文件(无需将其键入 Visual Studio),您可以创建一个可执行文件来读取您的源文件并从中创建一个 .resources 文件:

string path = Path.GetFullPath(Path.Combine(outputPath, "..\\MyData.resources"));

using (IResourceWriter rsxw = new ResourceWriter(path))
{
    foreach (var x ...)
    {
       rsxw.AddResource(x.name, x.value);
    }
    rsxw.Close();
}

在您的项目中包含这个 MyData.resources 文件,它将被编译为资源。

【讨论】:

  • 这听起来不错,但如果资源管理器使用集合,它在应用程序初始化期间将以相同的方式工作。我会试一试的。
  • 它在内部使用流从程序集文件中读取数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-10
  • 2021-09-16
  • 2023-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多