如何在C#中使用VirtualAlloc分配和使用本机内存?

时间:2012-12-31 00:20:00

标签: c# performance pinvoke

为了最大限度地提高关键方法的性能,in another question it was suggested我分配并使用本机内存而不是修复它。

我在C#工作,没有使用不安全代码和使用P / invoke的经验。我没有在Google上找到任何相关示例,MSDN article about VirtualAlloc也没有帮助。

这是一种被称为十亿次的方法,并且每一点性能都是可取的。

    public static readonly int[] HR = new int[32487834];
    public unsafe int eval(int c1, int c2, int c3, int c4, int c5, int c6, int c7)
    {
        fixed (int* HR = Evaluator.HR)
        {
            int p = HR[53 + c1];
            p = HR[p + c2];
            p = HR[p + c3];
            p = HR[p + c4];
            p = HR[p + c5];
            p = HR[p + c6];
            return (HR[p + c7]);
        }
    }

如果您感兴趣,它是TwoPlusTwo-Evaluator的C#端口,它使用123mb查找表返回随机7张牌扑克的等级进行比较。在我的机器上,我以随机顺序平均大约80M评估/ s,并且连续顺序(c1 = 0,c2 = 1,循环递增每个变量,最多52个)的500M评估/秒。

2 个答案:

答案 0 :(得分:1)

您的示例的主要问题是您必须使用fixed指令 来自外部 循环代码。在这个嵌套函数中使用它不会真正有用,并且可能会使事情变得更糟,因为它会使C#在内部调用其GC内存固定API。所以一个更好的解决方案就是这样的代码:

public unsafe int eval(int* HR, int c1, int c2, int c3, int c4, int c5, int c6, int c7)
{
    int p = HR[53 + c1];
    p = HR[p + c2];
    p = HR[p + c3];
    p = HR[p + c4];
    p = HR[p + c5];
    p = HR[p + c6];
    return (HR[p + c7]);
}

fixed本质上是GCHandle.AddrOfPinnedObject的语言构造同义词。在你的情况下,省略了对8个数组提取的边界检查,每次进入该功能时,都会通过GC固定和取消固定内存的成本...但可能不是很多。

使用P / Invoke通常并不简单,除非你想花几天时间熟悉这个领域,否则我会建议不要这样做。如果您还不熟悉通过C ++进行的Win32 / WinAPI编程,则更具挑战性。最好重新编写代码,以便尽可能将内存固定在链上。出于这个特定目的,您甚至可以为该数组创建一个GCHandle作为对象构造函数的一部分。

答案 1 :(得分:0)

尝试使用C ++ / Cli:

ref class Evaluator
{
public:
  static Int32* HR;
  static Evaluator()
  {
    int size = 32487834;
    HR = (INT*)malloc(size * 4);
  }

  static Int32 Eval(Int32 c1, Int32 c2, Int32 c3, Int32 c4, Int32 c5, Int32 c6, Int32 c7)
  {
      Int32 p = HR[53 + c1];
      p = HR[p + c2];
      p = HR[p + c3];
      p = HR[p + c4];
      p = HR[p + c5];
      p = HR[p + c6];
      return (HR[p + c7]);

  }
};