使用内存映射文件来补偿C#中的内存不足

本文关键字:内存不足 补偿 内存 映射 文件 | 更新日期: 2023-09-27 18:29:17

我遇到了一个问题——以前我一直在寻找一种算法来解决其中的一部分(请参见组合LINQ查询),但我遇到了巨大的问题。

在大约540k个目录中,它因内存不足而崩溃。:(

我正在尝试处理和存储公司的SAN文件信息,我们需要这样做,因为我们有一些人可以将数据保存25年,他们不需要,但很难跟踪。总共有70个;TB的文件。所以,正如你所能想象的,它有很多文件。

然而,从我所读到的内容来看,内存映射文件不可能是动态的吗?这是真的吗?我之前不知道有多少文件+目录是肯定的。

如果没有,(请说没有),有人能给我举一个关于如何制作动态映射文件的简短例子吗(CombineLINQ查询问题中提供的代码)。简而言之,我在内存中创建了一个目录结构,其中包含目录→目录+文件(名称、大小、访问日期、修改日期和创建日期)。

任何线索都将不胜感激,因为如果可能的话,这将绕过我的问题。

使用内存映射文件来补偿C#中的内存不足

当您无法将整件事放入内存时,您可以使用IEnumerable来流式传输数据。下面是一个例子。自从我需要最后一滴性能后,我也一直在玩MemoryMapped文件,但到目前为止,我一直使用BinaryReader/Writer。

对于DB的拥护者来说:当你真的需要最后一滴perf时,我也会自己做二进制文件。对数据库进行进程外处理确实会增加开销。此外,整个安全性/日志记录、ACID等都会增加。

下面是一个流式传输f_results类的示例。

编辑

更新的示例显示如何写入/读取目录信息树。我保留了一个包含所有目录的文件。这个树一次加载到内存中,然后指向所有f_results所在的文件。您仍然需要为每个目录创建一个单独的文件,其中包含所有文件的f_results。如何做到这一点取决于您的代码,但您应该能够弄清楚。

祝你好运!

public class f_results {
    public String name { get; set; }
    public DateTime cdate { get; set; }
    public DateTime mdate { get; set; }
    public DateTime adate { get; set; }
    public Int64 size { get; set; }
    // write one to a file
    public void WriteTo(BinaryWriter wrtr) {
        wrtr.Write(name);
        wrtr.Write(cdate.Ticks);
        wrtr.Write(mdate.Ticks);
        wrtr.Write(adate.Ticks);
        wrtr.Write(size);
    }
    // read one from a file
    public f_results(BinaryReader rdr) {
        name = rdr.ReadString();
        cdate = new DateTime(rdr.ReadInt64());
        mdate = new DateTime(rdr.ReadInt64());
        adate = new DateTime(rdr.ReadInt64());
        size = rdr.ReadInt64();
    }
    // stream a whole file as an IEnumerable (so very little memory needed)
    public static IEnumerable<f_results> FromFile(string dataFilePath) {
        var file = new FileStream(dataFilePath, FileMode.Open);
        var rdr = new BinaryReader(file);
        var eos = rdr.BaseStream.Length;
        while (rdr.BaseStream.Position < eos) yield return new f_results(rdr);
        rdr.Close();
        file.Close();
    }
}
class Program {
    static void Main(string[] args) {
        var d1 = new DirTree(@"C:'",
            new DirTree(@"C:'Dir1",
                new DirTree(@"C:'Dir1'Dir2"),
                new DirTree(@"C:'Dir1'Dir3")
                ),
                new DirTree(@"C:'Dir4",
                new DirTree(@"C:'Dir4'Dir5"),
                new DirTree(@"C:'Dir4'Dir6")
                ));
        var path = @"D:'Dirs.dir";
        // write the directory tree to a file
        var file = new FileStream(path, FileMode.CreateNew | FileMode.Truncate);
        var w = new BinaryWriter(file);
        d1.WriteTo(w);
        w.Close();
        file.Close();
        // read it from the file
        var file2 = new FileStream(path, FileMode.Open);
        var rdr = new BinaryReader(file2);
        var d2 = new DirTree(rdr);
        // now inspect d2 in debugger to see that it was read back into memory
        // find files bigger than (roughly) 1GB
        var BigFiles = from f in f_results.FromFile(@"C:'SomeFile.dat")
                       where f.size > 1e9
                       select f;
    }
}
class DirTree {
    public string Path { get; private set; }
    private string FilesFile { get { return Path.Replace(':', '_').Replace('''', '_') + ".dat"; } }
    public IEnumerable<f_results> Files() {
        return f_results.FromFile(this.FilesFile);
    }
    // you'll want to encapsulate this in real code but I didn't for brevity
    public DirTree[] _SubDirectories;
    public DirTree(BinaryReader rdr) {
        Path = rdr.ReadString();
        int count = rdr.ReadInt32();
        _SubDirectories = new DirTree[count];
        for (int i = 0; i < count; i++) _SubDirectories[i] = new DirTree(rdr);
    }
    public DirTree( string Path, params DirTree[] subDirs){
        this.Path = Path;
        _SubDirectories = subDirs;
    }
    public void WriteTo(BinaryWriter w) {
        w.Write(Path);           
        w.Write(_SubDirectories.Length);
        // depth first is the easiest way to do this
        foreach (var f in _SubDirectories) f.WriteTo(w);
    }
}

}