2012-03-28 3 views
1

에있는 메모리 부족을 보완했습니다. 이전에는 문제의 일부를 해결하기 위해 알고리즘을 사용한 적이 있었지만 (Combine LINQ queries 참조) 어쨌든 큰 문제가 발생했습니다.메모리 맵핑 된 파일을 사용하여 C#

약 540k 디렉토리에서 메모리 부족으로 고장났습니다. : (

저는 회사의 SAN 파일 정보를 처리하고 저장하려고합니다. 우리는 25 년간 데이터를 보관하고 필요하지는 않지만 추적하기 어려운 사람들이 있기 때문에이 작업을 수행해야합니다. 총 70 개의 파일이 있습니다.

내가 읽은 바로는 메모리 매핑 된 파일은 동적 일 수 없습니까? 사실입니까? 나는 얼마나 많은 파일 + 디렉토리가 있는지 미리 알 수 없다.

그렇지 않다면, 누군가가 나에게 동적 매핑 파일을 만드는 방법에 대한 간단한 예제를 제공 할 수 있는가? (Combine LINQ queries 질문). 즉, 디렉토리 → 디렉토리 + 파일 (이름, 크기, 액세스 날짜, 수정 날짜 및 생성 날짜)을 보유한 메모리에 디렉토리 구조를 만듭니다.

가능한 경우 내 문제를 해결할 수있는 모든 단서를 높이 평가할 것입니다.

+1

당신은 메모리 내 표현 대신 데이터베이스를 사용하여 생각 해 봤나? –

+0

네, 가지고 있었지만, 실행하고 싶었던 쿼리는 디렉토리를 읽거나 디렉토리의 크기를 합산하는 등 매우 까다로워졌습니다. – BugFinder

+0

여전히 최선의 방법 일 것입니다. 확실히해라. 이러한 유형의 문제를 해결하는 것은 데이터베이스입니다. 데이터 수명이 짧다는 것을 제외하고는 –

답변

2

메모리에 모든 것을 넣을 수없는 경우 IEnumerable을 사용하여 데이터를 스트리밍 할 수 있습니다. 아래 예제를 참조하십시오. 나는 perf의 마지막 드롭이 필요하기 때문에 MemoryMapped 파일을 가지고 놀았지만 지금까지는 BinaryReader/Writer를 고수했습니다.

DB 옹호자의 경우 : 마지막으로 perf를 사용해야 할 때 필자는 자체 바이너리 파일도 만듭니다. 프로세스를 DB로 옮기면 실제로 오버 헤드가 추가됩니다. 또한 전체 보안/로깅, ACID 등이 합쳐집니다.

다음은 f_results 클래스를 스트리밍하는 예제입니다.

편집

업데이트 예/쓰기 디렉토리 정보 트리를 읽는 방법을 표시합니다. 모든 디렉토리를 보유하는 1 개의 파일을 보관합니다. 이 트리는 한 번에 메모리에로드 된 다음 모든 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); 
    } 
} 

은}

+0

먼저 메모리 맵 파일로 시작하기 전에 RAM 디스크를 사용합니다. – weismat

+0

SSD가 있습니다. 나는 32Bit 만 (은행)으로 제공되는 커스텀 윈도우 빌드에있다. 그래서 나는 너무 많은 memeory를 먹을 것이다. 나는 두려워한다. – gjvdkamp

+0

이것은 유망 해 보이지만, 내 디렉토리 구조를 잃어 버릴 수 있습니다. 그러나 이것은 나에게 확실히 아이디어를주었습니다! 고맙습니다! – BugFinder

관련 문제