2

スペースで区切られた数字のファイルがあります。サイズは約1Gbで、そこから数値を取得したいと思います。高速に読み取るためにメモリ マップ ファイルを使用することにしましたが、その方法がわかりません。私は次にやろうとしました:

var mmf = MemoryMappedFile.CreateFromFile("test", FileMode.Open, "myFile");
var mmfa = mmf.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read);
var nums = new int[6];
var a = mmfa.ReadArray<int>(0, nums, 0, 6); 

しかし、「test」の num[0] に「01」だけが含まれている場合、12337 が得られます。12337 = 48*256+49 です。インターネットで検索しましたが、私の質問について何も見つかりませんでした。バイト配列またはプロセス間通信についてのみ。num[0] で 1 を取得する方法を教えてもらえますか?

4

3 に答える 3

3

次の例では、文字列を作成せずに、可能な限り高速な方法でメモリ マップ ファイルから ASCII 整数を読み取ります。MiMo が提供するソリューションは、はるかに低速です。5 MB/秒で動作しますが、あまり役に立ちません。MiMo ソリューションの最大の問題は、char ごとにメソッド (Read) を呼び出すため、15 倍のパフォーマンスが必要になることです。元の問題がパフォーマンスの問題だったのに、なぜ彼の解決策を受け入れたのだろうか。ダム文字列リーダーを使用して文字列を整数に解析すると、20 MB/秒を取得できます。メソッド呼び出しを介してすべてのバイトを取得すると、可能な読み取りパフォーマンスが損なわれます。

以下のコードは、32 ビット アドレス空間がいっぱいになるのを防ぐために、ファイルを 200 MB のチャンクでマップします。次に、非常に高速なバイト ポインターを使用してバッファーをスキャンします。ローカリゼーションを考慮しなければ、整数の解析は簡単です。興味深いのは、マッピングのビューを作成した場合、ビュー バッファーへのポインターを取得する唯一の方法では、マップされた領域から開始できないことです。

これは、.NET 4.5 でまだ修正されていない.NET Framworkのバグだと思います。SafeMemoryMappedViewHandle バッファは、OS の割り当て粒度で割り当てられます。あるオフセットに進むと、まだバッファの先頭を指しているポインタが返されます。これは、解析パフォーマンスで 5MB/s と 77MB/s の違いを生むため、非常に残念です。

Did read 258.888.890 bytes with 77 MB/s


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

unsafe class Program
{
    static void Main(string[] args)
    {
        new Program().Start();
    }

    private void Start()
    {
        var sw = Stopwatch.StartNew();
        string fileName = @"C:\Source\BigFile.txt";//@"C:\Source\Numbers.txt";
        var file = MemoryMappedFile.CreateFromFile(fileName);
        var fileSize = new FileInfo(fileName).Length;
        int viewSize = 200 * 100 * 1000;
        long offset = 0;
        for (; offset < fileSize-viewSize; offset +=viewSize ) // create 200 MB views
        {
            using (var accessor = file.CreateViewAccessor(offset, viewSize))
            {
                int unReadBytes = ReadData(accessor, offset);
                offset -= unReadBytes;
            }
        }

        using (var rest = file.CreateViewAccessor(offset, fileSize - offset))
        {
            ReadData(rest, offset);
        }
        sw.Stop();
        Console.WriteLine("Did read {0:N0} bytes with {1:F0} MB/s", fileSize, (fileSize / (1024 * 1024)) / sw.Elapsed.TotalSeconds);
    }


    List<int> Data = new List<int>();

    private int ReadData(MemoryMappedViewAccessor accessor, long offset)
    {
        using(var safeViewHandle = accessor.SafeMemoryMappedViewHandle)
        {
            byte* pStart = null;
            safeViewHandle.AcquirePointer(ref pStart);
            ulong correction = 0;
            // needed to correct offset because the view handle does not start at the offset specified in the CreateAccessor call
            // This makes AquirePointer nearly useless.
            // http://connect.microsoft.com/VisualStudio/feedback/details/537635/no-way-to-determine-internal-offset-used-by-memorymappedviewaccessor-makes-safememorymappedviewhandle-property-unusable
            pStart = Helper.Pointer(pStart, offset, out correction);
            var len = safeViewHandle.ByteLength - correction;
            bool digitFound = false;
            int curInt = 0;
            byte current =0;
            for (ulong i = 0; i < len; i++)
            {
                current = *(pStart + i);
                if (current == (byte)' ' && digitFound)
                {
                    Data.Add(curInt);
                  //  Console.WriteLine("Add {0}", curInt);
                    digitFound = false;
                    curInt = 0;
                }
                else
                {
                    curInt = curInt * 10 + (current - '0');
                    digitFound = true;
                }
            }

            // scan backwards to find partial read number
            int unread = 0;
            if (curInt != 0 && digitFound)
            {
                byte* pEnd = pStart + len;
                while (true)
                {
                    pEnd--;
                    if (*pEnd == (byte)' ' || pEnd == pStart)
                    {
                        break;
                    }
                    unread++;

                }
            }

            safeViewHandle.ReleasePointer();
            return unread;
        }
    }

    public unsafe static class Helper
    {
        static SYSTEM_INFO info;

        static Helper()
        {
            GetSystemInfo(ref info);
        }

        public static byte* Pointer(byte *pByte, long offset, out ulong diff)
        {
            var num = offset % info.dwAllocationGranularity;
            diff = (ulong)num; // return difference

            byte* tmp_ptr = pByte;

            tmp_ptr += num;

            return tmp_ptr;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo);

        internal struct SYSTEM_INFO
        {
            internal int dwOemId;
            internal int dwPageSize;
            internal IntPtr lpMinimumApplicationAddress;
            internal IntPtr lpMaximumApplicationAddress;
            internal IntPtr dwActiveProcessorMask;
            internal int dwNumberOfProcessors;
            internal int dwProcessorType;
            internal int dwAllocationGranularity;
            internal short wProcessorLevel;
            internal short wProcessorRevision;
        }
    }

    void GenerateNumbers()
    {
        using (var file = File.CreateText(@"C:\Source\BigFile.txt"))
        {
            for (int i = 0; i < 30 * 1000 * 1000; i++)
            {
                file.Write(i.ToString() + " ");
            }
        }
    }

}
于 2012-05-05T19:56:59.957 に答える
1

ファイルの内容を解析して、文字を数字に変換する必要があります-次のように:

List<int> nums = new List<int>();
long curPos = 0;
int curV = 0;
bool hasCurV = false;
while (curPos < mmfa.Capacity) {
  byte c;
  mmfa.Read(curPos++, out c);
  if (c == 0) {
    break;
  }
  if (c == 32) {
    if (hasCurV) {
      nums.Add(curV);
      curV = 0;
    }
    hasCurV = false;
  } else {
    curV = checked(curV*10 + (int)(c-48));
    hasCurV = true;
  }
}
if (hasCurV) {
  nums.Add(curV);
}

mmfa.Capacityこれが読み取る文字の総数であり、ファイルにはスペースで区切られた数字のみが含まれている (つまり、終了行やその他の空白がない) と仮定します。

于 2012-05-05T19:46:29.440 に答える