2

我正在使用 Intel Ivy Bridge CPU 并想使用 RDRAND 操作码(https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide ) 在 C# 中。

如何通过 C# 调用此 CPU 指令?我在这里看到了从 c# 执行汇编代码的示例:x86/x64 CPUID in C#

但我不确定如何将它用于 RDRAND。代码不需要检查执行代码的 CPU 是否支持该指令。

我已经看到了这个执行来自Intel 的drng_samples的汇编字节码的 C++ 示例:

int rdrand32_step (uint32_t *rand)
{
    unsigned char ok;

    /* rdrand edx */
    asm volatile(".byte 0x0f,0xc7,0xf0; setc %1"
        : "=a" (*rand), "=qm" (ok)
        :
        : "edx"
    );

    return ok;
}

在 C# 中执行汇编代码的示例如何与来自 Inteldrng示例代码的 C++ 代码相结合?

4

1 回答 1

6

SO上有一些答案将在运行时生成(非托管)汇编代码,以便托管代码回调。这一切都非常有趣,但我建议您只需为此目的使用 C++/CLI,因为它旨在简化互操作场景。创建一个新的 Visual C++ CLR 类库并给它一个rdrandwrapper.cpp

#include <immintrin.h>

using namespace System;

namespace RdRandWrapper {

#pragma managed(push, off)
  bool getRdRand(unsigned int* pv) {
    const int max_rdrand_tries = 10;
    for (int i = 0; i < max_rdrand_tries; ++i) {
      if (_rdrand32_step(pv)) return true;
    }
    return false;
  }
#pragma managed(pop)

  public ref class RandomGeneratorError : Exception
  {
  public:
    RandomGeneratorError() : Exception() {}
    RandomGeneratorError(String^ message) : Exception(message) {}
  };

  public ref class RdRandom
  {
  public:
    int Next() {
      unsigned int v;
      if (!getRdRand(&v)) {
        throw gcnew RandomGeneratorError("Failed to get hardware RNG number.");
      }
      return v & 0x7fffffff;
    }
  };
}

这是一个非常简单的实现,只是试图模仿Random.Next获取单个非负随机整数。根据问题,它不会尝试验证RDRANDCPU 上实际可用,但它确实处理了指令存在但无法工作的情况。(除非它被破坏,否则这在当前硬件上“不会发生”,详见此处。)

生成的程序集是可以由托管 C# 代码使用的混合程序集。确保将程序集编译为 x86 或 x64,与非托管代码相同(默认情况下,项目设置为编译为“任何 CPU”,由于非托管代码只有一个特定位数,因此无法正常工作)。

using System;
using RdRandWrapper;

class Program {
  static void Main(string[] args) {
    var r = new RdRandom();
    for (int i = 0; i != 10; ++i) {
      Console.WriteLine(r.Next());
    }
  }
}

我对性能没有任何要求,但它可能不是很好。如果您想以这种方式获取许多随机值,您可能需要Next(int[] values)重载以在一次调用中获取许多随机值,以减少互操作的开销。

于 2017-08-02T20:32:06.533 回答