0

アンマネージド文字列をマネージド文字列にすばやく変換できる関数を探しています。を見ていましたMarshal.PtrToStringAnsiが、本当に遅いです。

.NET フレームワークのソース コードに、次の定義があります。

public static String PtrToStringAnsi(IntPtr ptr)
{
    if (Win32Native.NULL == ptr) {
        return null; 
    }
    else if (IsWin32Atom(ptr)) { 
        return null; 
    }
    else { 
        int nb = Win32Native.lstrlenA(ptr);
        if( nb == 0) {
            return string.Empty;
        } 
        else {
            StringBuilder sb = new StringBuilder(nb); 
            Win32Native.CopyMemoryAnsi(sb, ptr, new IntPtr(1+nb)); 
            return sb.ToString();
        } 
    }
}

アプリケーションのパフォーマンスを向上させるために、はるかに高速な Marshal.PtrToStringAnsi(IntPtr, int) メソッドを使用する次のメソッドを作成しました。

[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, EntryPoint = "lstrlenA")]
[ResourceExposure(ResourceScope.None)]
internal static extern int lstrlenA(IntPtr ptr); 

static public string PtrToString( IntPtr p )
{
   if (p == IntPtr.Zero)
      return null;
   int len = lstrlenA(p);
   if (len == 0)
      return string.Empty;
   return Marshal.PtrToStringAnsi(p, len);
}

この代替手段ははるかに高速に見えます。そもそも Microsoft が PtrToStringAnsi 関数をコーディングしなかった理由はありますか? 私はおそらく何か重要なものを見逃しています...

4

1 に答える 1

1

違いは への呼び出しIsWin32Atomです。あなたのバージョンはそれを省略しています。元に戻すと、バージョンが のバージョンと同等であることがわかりますMarshal。呼び出しを削除しても IsWin32Atom、パフォーマンスの向上はごくわずかです。

テスト プログラムの私のバージョンは次のようになります。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace Test
{
    internal class Program
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, EntryPoint = "lstrlenA")]
        [ResourceExposure(ResourceScope.None)]
        internal static extern int lstrlenA(IntPtr ptr);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern System.IntPtr GetCommandLine();

        private static readonly IntPtr HIWORDMASK = unchecked(new IntPtr((long)0xffffffffffff0000L));

        private static bool IsWin32Atom(IntPtr ptr)
        {
            long num = (long)ptr;
            return 0 == (num & (long)HIWORDMASK);
        }

        public static string PtrToString(IntPtr p)
        {
            if (p == IntPtr.Zero)
                return null;
            if (IsWin32Atom(p))
                return null;
            int len = lstrlenA(p);
            if (len == 0)
                return String.Empty;
            return Marshal.PtrToStringAnsi(p, len);
        }

        private static void Main(string[] args)
        {
            var p = Marshal.StringToHGlobalAnsi("Console.WriteLine(\"Marshal class: result={0} time={1}ms\", s, sw.ElapsedMilliseconds);");

            string s = "";
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (double i = 0; i < 5000000; i++)
            {
                s = Marshal.PtrToStringAnsi(p);
            }
            sw.Stop();
            Console.WriteLine("Marshal class: result={0} time={1}ms", s, sw.ElapsedMilliseconds);
            sw.Restart();
            for (double i = 0; i < 5000000; i++)
            {
                s = Program.PtrToString(p);
            }
            sw.Stop();
            Console.WriteLine("My implementation: result={0} time={1}ms", s, sw.ElapsedMilliseconds);
            Console.ReadLine();
        }
    }
}

実行時間は、実行ごとにかなり異なります。しかし、ここに典型的な出力があります:

マーシャル クラス: result=Console.WriteLine("マーシャル クラス: 結果={0} 時間={1}ミリ秒",
  s、sw.ElapsedMilliseconds); 時間=1914ms
私の実装: result=Console.WriteLine("Marshal class: result={0} time={1}ms",
  s、sw.ElapsedMilliseconds); 時間=2065ms

しかし、逆に出てくることもあります。要するに、どちらかを選択する必要はありません。

への呼び出しを削除するとIsWin32Atom、バージョンが勝つことが多くなります。しかし、それほどではありません。通常、速度には約 5% の差があります。Marshal.PtrToStringAnsiそれが「非常に遅い」と思う理由がわかりません。

Marshal.PtrToStringAnsiの 2 つのパラメーター バージョンは、本質的にelseコードの句であると非常に期待しています。

: 私のテスト環境は Win7 x64、VS2012、AnyCPU、Release でした。

于 2013-03-14T14:14:52.263 に答える