2014-04-07 3 views
0

매우 많은 양의 이진 데이터를 파일에서 읽어야합니다. 고정 된 레코드 크기 (38)가 있으며 한 번에 여러 레코드를 건너 뜁니다. FileStrea, Position 또는 Seek를 사용하여이 작업을 시도했지만 너무 시간이 많이 걸리는 것처럼 보입니다. 따라서 10 개의 레코드를 건너 뛰더라도 10 개의 파일을 읽지는 않습니다.바이너리 파일의 일부를 FileStream.Position보다 빨리 건너 뛸 수있는 방법이 있습니까?

여기에 SSCCE가 있습니다.

중재자 참고 : 이것은 반복 질문이 아니며, 다른 질문에서 추출한 후속 작업으로 다른 초점을 탐구 할 수 있습니다.

두 개의 직렬화 및 비 직렬화 버튼을 만들어야합니다.

Serialize는 더미 데이터 파일을 만듭니다.

역 직렬화를 통해 읽습니다.

전체 파일의 원시 읽기를 보려면 fs.Position 행을 주석 처리하십시오. 내 컴퓨터에서 12 초가 걸립니다. 그런 다음 주석 처리를 제거하면 매번 10 개의 레코드가 건너 뜁니다. 속도가 10 배 향상 되길 바랬지 만, 내 컴퓨터에서는 8 초가 걸렸습니다. 그래서 나는 변화 fs.Position 비싸다고 가정합니다.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using ProtoBuf; 
using System.IO; 
using System.Diagnostics; 

namespace BinTest3 
{ 


    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void Serialize_Click(object sender, EventArgs e) 
     { 

      FileStream outBin = null; 

      string binFileName = @"C:\binfile.dft"; 
      outBin = File.Create(binFileName, 2048, FileOptions.None); 

      DateTime d = DateTime.Now; 

      TickRecord tr = new TickRecord(d, 1.02, 1.03,200,300); 

      for (int i =0; i < 20000000; i++) 
      { 
       tr.BidPrice += 1; 
       Serializer.SerializeWithLengthPrefix(outBin, tr, PrefixStyle.Base128); 
      } 

      outBin.Close(); 
      label1.Text = "Done "; 
     } 

     private void Deserialize_Click(object sender, EventArgs e) 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 

      FileStream fs; 
      string binFileName = @"C:\binfile.dft"; 

      fs = new FileStream(binFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4 * 4096); 
      long skipRate =10; 
      int count = 0; 
      TickRecord tr; 

      long skip = (38*skipRate); 
      try 
      { 
       while ((tr = Serializer.DeserializeWithLengthPrefix<TickRecord>(fs, PrefixStyle.Base128)) != null) //fs.Length > fs.Position) 
       { 
        count++; 

        fs.Position += skip; //Comment out this line to see raw speed 

       } 
      } 
      catch (Exception) 
      { 

      } 

      fs.Close(); 

      sw.Stop(); 
      label1.Text = "Time taken: " + sw.Elapsed + " Count: " + count.ToString("n0"); 

     } 
    } 


    [ProtoContract] 
    public class TickRecord 
    { 

     [ProtoMember(1, DataFormat = DataFormat.FixedSize)] 
     public DateTime DT; 
     [ProtoMember(2)] 
     public double BidPrice; 
     [ProtoMember(3)] 
     public double AskPrice; 
     [ProtoMember(4, DataFormat = DataFormat.FixedSize)] 
     public int BidSize; 
     [ProtoMember(5, DataFormat = DataFormat.FixedSize)] 
     public int AskSize; 

     public TickRecord() 
     { 

     } 

     public TickRecord(DateTime DT, double BidPrice, double AskPrice, int BidSize, int AskSize) 
     { 
      this.DT = DT; 
      this.BidPrice = BidPrice; 
      this.AskPrice = AskPrice; 
      this.BidSize = BidSize; 
      this.AskSize = AskSize; 

     } 



    } 
} 
+0

디스크를 찾아서 ** 모든 시간을 디스크 읽기에 소비하지 않아야합니다 **. 1 개의 레코드 만 읽더라도 1 밀리 초 시간이 없어도 –

+0

아니요, 더 빠른 방법은 없습니다. 빠른 탐색을 원하면 SSD를 사용하십시오. 또한, 당신이하는 것처럼 많은 작은 탐색을하는 것은 특히 비효율적입니다. 파일의 큰 부분을 건너 뛰면 훨씬 큰 차이가 있음을 알 수 있습니다. –

답변

4

디스크는 2 바이트를 읽는 것보다 빠르게 1 바이트를 읽을 수 없습니다. 디스크는 한 번에 큰 덩어리를 읽어야합니다. 따라서 소수의 레코드를 건너 뛰어도 실제로 성능이 변경되지는 않습니다. 따라서 최소 한도의 데이터까지 단일 읽기에 대해 고정 가격을 지불하게됩니다. 이 크기는 디스크마다 다릅니다.

게다가 파일 API를 호출하는 데 상당한 오버 헤드가 있습니다. 한 번에 적은 금액 만 읽으면 그 간접비를 반복해서 지불하게됩니다. 코드에서 버퍼링을 구현하는 것이 좋습니다. 많은 양의 데이터를 메모리로 읽어 들인 다음 메모리에서 실제 읽기를 분석하십시오. 아마도 이것을 구현하는 가장 효율적인 방법은 memory mapped file을 사용하는 것입니다.

+0

사실 저는 readallbytes를 사용하여 memorystream을 모두 읽었습니다. 그리고 memorystream을 찾고 있습니다. 그건 내가 바라는대로 잘 작동한다. - 고마워요. – ManInMoon

+0

파일이 다른 문제없이 주 메모리에 맞을만큼 작 으면 아주 합리적인 접근법입니다. –