简体   繁体   中英

How to use Intel's RDRAND using inline assembly with .Net

I'm using an Intel Ivy Bridge CPU and want to use the RDRAND opcode ( https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide ) in C#.

How can I call this CPU instruction via C#? I've seen an example of executing assembly code from c# here: x86/x64 CPUID in C#

But I'm not sure how to use it for RDRAND. The code doesn't need to check whether the CPU executing the code supports the instruction or not.

I've seen this C++ example of executing assembly byte code coming from drng_samples of Intel:

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;
}

How can the example of executing assembly code in C# be combined with the C++ code coming from the Intel drng sample code?

There are answers out on SO that will generate (unmanaged) assembly code at runtime for managed code to call back into. That's all very interesting, but I propose that you simply use C++/CLI for this purpose, because it was designed to simplify interop scenarios. Create a new Visual C++ CLR class library and give it a single 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;
    }
  };
}

This is a very bare-bones implementation that just tries to mimic Random.Next in getting a single non-negative random integer. Per the question, it does not attempt to verify that RDRAND is actually available on the CPU, but it does handle the case where the instruction is present but fails to work. (This "cannot happen" on current hardware unless it's broken, as detailed here .)

The resulting assembly is a mixed assembly that can be consumed by managed C# code. Make sure to compile your assembly as either x86 or x64, same as your unmanaged code (by default, projects are set to compile as "Any CPU", which will not work correctly since the unmanaged code has only one particular bitness).

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());
    }
  }
}

I make no claims as to performance, but it's probably not great. If you wanted to get many random values this way, you would probably want a Next(int[] values) overload to get many random values in one call, to reduce the overhead of interop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM