Click here to Skip to main content
Click here to Skip to main content
Go to top

SFMT in Action: Part II – Object oriented implementation of SIMD-oriented Fast Mersenne Twister (SFMT) in C#

, 31 Oct 2009
Rate this:
Please Sign up or sign in to vote.
The new SIMD-oriented Fast Mersenne Twister (SFMT) library was developed using object oriented technologies such as UML, composition, aggregation, generalization, multiplicity, and Design Patterns.

Contents

Introduction

At the end, after a long time, I've just finished the second part of the SFMT in Action series. The first part of this series was about producing SFMT (SIMD-oriented Fast Mersenne Twister) DLLs and generating pseudo random numbers using these DLLs. The most important point of the first article was the acceleration of generating integers via the SSE2 support of CPU. Also, the first SFMT In Action article was essential to continue on my work. You can find the first article here. Today, I'll explain how the SFMT DLLs written in C/C++ can be used from C# projects efficiently.

Approach

In this article, I'll add new Visual Studio 2008 projects to my solution. I'll describe all of the projects and their details below. The new project outputs (DLLs) will be used by other projects. Also, I'll describe and explain the object-oriented structure of my project. At the end of the article, you'll able to generate random SFMT integers from your C# applications via the new PRNG library.

Early Flowchart

Below, you can see the early version of the C# SFMT class. It's quite primitive and faraway from being in an object-oriented structure. The current SFMT class structure and algorithm was changed highly, but its concept of using SSE2 is the same as in the old SFMT class concept. This old one was a reference to my current project. My aim to publish this algorithm here is to give you some idea about it.

All-In-One Code Framework and UnmanagedLibrary

Please remember this: in a C# project, you couldn't call a C/C++ DLL directly. Code in the C/C++ DLL is unmanaged. Also, you couldn't add a C/C++ DLL as a reference to a C# project. Therefore, before starting, I needed a bridge between my C# library and my C/C++ SFMT DLLs. Some experienced developers know that this is easy with a [DllImport(_dllLocation)] attribute in C#. But I need an object-oriented design based on a much stronger concept than just adding this attribute.

In the middle of 2009, I heard about a framework named All-In-One Code Framework. All-In-One Code Framework code-named CodeFx delineates the framework and skeleton of most Microsoft development techniques (e.g., COM, Data Access, IPC) using typical sample codes in different programming languages (e.g., Visual C#, VB.NET, Visual C++). You can look into this framework here. In this framework, there is the CSLoadLibrary project in C# which dynamically loads a native DLL in it. In this project, the UnmanagedLibrary class is designed very well, and has everything for calling C/C++ DLL functions from C# code easily. You can find the entire content of the class here . I'll use this class in my project as a bridge between SFMT DLLs and my C# code. Let's talk about the important parts of this class:

#region Using directives
using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
#endregion


/// <summary>
/// Utility class to wrap an unmanaged DLL and be responsible for freeing it.
/// </summary>
/// <remarks>
/// This is a managed wrapper over the native LoadLibrary, GetProcAddress, 
/// and FreeLibrary calls.
/// </example>
/// <see cref=
/// "http://blogs.msdn.com/jmstall/archive/2007/01/06/Typesafe-GetProcAddress.aspx"
/// />
public sealed class UnmanagedLibrary : IDisposable
{
    #region Safe Handles and Native imports

    // See http://msdn.microsoft.com/msdnmag/issues/05/10/Reliability/ 
    // for more about safe handles.
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        /// <summary>
        /// Create safe library handle
        /// </summary>
        private SafeLibraryHandle() : base(true) { } // SafeLibraryHandle

        /// <summary>
        /// Release handle
        /// </summary>
        protected override bool ReleaseHandle()
        {
            return NativeMethods.FreeLibrary(handle);
        } // ReleaseHandle()

    } // class SafeLibraryHandle

    /// <summary>
    /// Native methods
    /// </summary>
    static class NativeMethods
    {
        const string s_kernel = "kernel32";
        [DllImport(s_kernel, CharSet = CharSet.Auto,
            BestFitMapping = false, SetLastError = true)]
        public static extern SafeLibraryHandle LoadLibrary(string fileName);

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [DllImport(s_kernel, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FreeLibrary(IntPtr hModule);

        [DllImport(s_kernel, EntryPoint = "GetProcAddress")]
        public static extern IntPtr GetProcAddress(
            SafeLibraryHandle hModule, String procname);
    } // class NativeMethods

    #endregion // Safe Handles and Native imports


    /// <summary>
    /// Constructor to load a dll and be responible for freeing it.
    /// </summary>
    /// <param name="fileName">full path name of dll to load</param>
    /// <exception cref="System.IO.FileNotFound">
    /// If fileName can't be found
    /// </exception>
    /// <remarks>
    /// Throws exceptions on failure. Most common failure would be 
    /// file-not-found, or that the file is not a loadable image.
    /// </remarks>
    public UnmanagedLibrary(string fileName)
    {
        m_hLibrary = NativeMethods.LoadLibrary(fileName);
        if (m_hLibrary.IsInvalid)
        {
            int hr = Marshal.GetHRForLastWin32Error();
            Marshal.ThrowExceptionForHR(hr);
        }
    } // UnmanagedLibrary(fileName)

    /// <summary>
    /// Dynamically lookup a function in the dll via kernel32!GetProcAddress.
    /// </summary>
    /// <param name="functionName">
    /// raw name of the function in the export table.
    /// </param>
    /// <returns>
    /// null if function is not found. Else a delegate to the unmanaged 
    /// function.
    /// </returns>
    /// <remarks>
    /// GetProcAddress results are valid as long as the dll is not yet 
    /// unloaded. This is very very dangerous to use since you need to 
    /// ensure that the dll is not unloaded until after you're done with any 
    /// objects implemented by the dll. For example, if you get a delegate 
    /// that then gets an IUnknown implemented by this dll, you can not 
    /// dispose this library until that IUnknown is collected. Else, you may 
    /// free the library and then the CLR may call release on that IUnknown 
    /// and it will crash.
    /// </remarks>
    public TDelegate GetUnmanagedFunction<TDelegate>(string functionName)
        where TDelegate : class
    {
        IntPtr p = NativeMethods.GetProcAddress(m_hLibrary, functionName);

        // Failure is a common case, especially for adaptive code.
        if (p == IntPtr.Zero)
        {
            return null;
        }

        Delegate function = Marshal.GetDelegateForFunctionPointer(
            p, typeof(TDelegate));

        // Ideally, we'd just make the constraint on TDelegate be
        // System.Delegate, but compiler error CS0702 
        // (constrained can't be System.Delegate)
        // prevents that. So we make the constraint system.object and do the
        // cast from object-->TDelegate.
        object o = function;

        return (TDelegate)o;
    } // GetUnmanagedFunction(, functionName)


    #region IDisposable Members

    /// <summary>
    /// Call FreeLibrary on the unmanaged dll. All function pointers handed 
    /// out from this class become invalid after this.
    /// </summary>
    /// <remarks>
    /// This is very dangerous because it suddenly invalidate everything
    /// retrieved from this dll. This includes any functions handed out via 
    /// GetProcAddress, and potentially any objects returned from those 
    /// functions (which may have an implemention in the dll).
    /// </remarks>
    public void Dispose()
    {
        if (!m_hLibrary.IsClosed)
        {
            m_hLibrary.Close();
        }
    } // Dispose()

    // Unmanaged resource. CLR will ensure SafeHandles get freed, without 
    // requiring a finalizer on this class.
    SafeLibraryHandle m_hLibrary;

    #endregion
}

Code Logic

  1. P/Invoke the API LoadLibrary to dynamically load a native DLL.
  2. P/Invoke the API GetProcAddress to get the function pointer of a specified function in the DLL's export table.
  3. Call Marshal.GetDelegateForFunctionPointer to convert the function pointer to a delegate object.
  4. Call the delegate.
  5. Call FreeLibrary on the unmanaged DLL.

Usage Example

//Delegate Method
delegate void aMethod();
//Field
UnmanagedLibrary _unmanagedLibrary;
AMethod _aMethod = null;

public AClass()
{
    _unmanagedLibrary = new UnmanagedLibrary(_dllLocation); 
    _aMethod = _unmanagedLibrary.GetUnmanagedFunction<aMethod>("aMethod");
}
~AClass()
{
    _unmanagedLibrary.Dispose();
}

As you can see, UnmanagedLibrary implements System.IDisposable. So, when we make an instance of the class, we can call the Dispose method easily to free the memory immediately.

CPUID and CpuCapabilities

As you know, my aim is to provide a fast pseudo random number generator using the SSE2 instruction set. Therefore, it's critical to detect SSE2 support of the OS and CPU. In the Program Files --> Microsoft Visual Studio 9.0 --> Samples folder, you can find CPUID.vcproj. This project is a C language project, and can be used to determine the capabilities of the CPU being run. Microsoft's original CPUID project includes two important files: cpuid.h and cpuid.c.

I opened my old SFMT solution (you can download it here) in Visual Studio 2008. Then, I clicked on it and added a new C++ project to my SFMT solution using Add --> New project --> Other Languages --> Visual C++ --> Class Library. I renamed my new C++ project to CPUID. After that, I added the cpuid.h and cpuid.c files to this CPUID project. You can see the screenshot below.

I also created a new file called SSE2.cpp in the CPUID project and added three important methods to it. Let's see the SSE2.cpp content:

#include <windows.h>
#include "cpuid.h"

_p_info info;

#ifdef __cplusplus
extern "C" {
#endif
    DllExport void getCPUProperties();
    DllExport int checkSSE2Feature();
    DllExport int checkSSE2Os();

    //This one gets CPU properties of your machine
    void getCPUProperties()
    {
        _cpuid(&info);
    }

    //You must call getCPUProperties before calling this function
    //This function checks SSE2 support of your CPU
    int checkSSE2Feature()
    {
        __try { 
            if (!info.checks & _CPU_FEATURE_SSE2) {
                return 0;
            }

            if (!info.feature & _CPU_FEATURE_SSE2) {
                return 0;
            }

            if (info.checks & _CPU_FEATURE_SSE2) {
                if (info.feature = _CPU_FEATURE_SSE2) {
                    return 1;
                }
            }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            return 0;
        }
    }

    //You must call getCPUProperties before calling this function
    //This function checks SSE2 support of Operating System
    int checkSSE2Os()
    {
        __try {
            if (!info.checks & _CPU_FEATURE_SSE2) {
                return 0;
            }

            if (!info.os_support & _CPU_FEATURE_SSE2) {
                return 0;
            }

            if (info.checks & _CPU_FEATURE_SSE2) {
                if (info.os_support = _CPU_FEATURE_SSE2) {
                    return 1;
                }
            }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            return 0;
        }
    }

#ifdef __cplusplus
}
#endif

The first function, getCPUProperties, gets the CPU properties of the machine. The second one, checkSSE2Feature, checks SSE2 support of your CPU and must be called after getCPUProperties. The last one, checkSSE2Os, checks the SSE2 support of the Operating System and must be called after getCPUProperties. Because of using the DllExport structure, all these methods can be called outside of CPUID.dll. I used these methods in the CpuCapabilities class. Let's look into this class:

CpuCapabilities

CpuCapabilities is a C# class that can access CPUID.dll and its methods using UnmanagedLibrary. I wrote this class before writing and calling any SFMT class structure. I organized the structure of the CpuCapabilities class to allow me to use it from other project classes. Below, you can see the entire content of the CpuCapabilities class:

public class CpuCapabilities
{
   #region Delegate Methods
   delegate void getCPUProperties();
   delegate int checkSSE2Feature();
   delegate int checkSSE2Os();
   #endregion

   #region Fields
   protected UnmanagedLibrary _unmanagedLibrary;
   getCPUProperties _getCpuProperties = null;
   checkSSE2Feature _checkSse2Feature = null;
   checkSSE2Os _checkSse2Os = null;
   #endregion

   public CpuCapabilities()
   {
      _unmanagedLibrary = new UnmanagedLibrary("CPUID");
      _getCpuProperties = 
        _unmanagedLibrary.GetUnmanagedFunction<getCPUProperties>("getCPUProperties");
      _checkSse2Feature = 
        _unmanagedLibrary.GetUnmanagedFunction<checkSSE2Feature>("checkSSE2Feature");
      _checkSse2Os = _unmanagedLibrary.GetUnmanagedFunction<checkSSE2Os>("checkSSE2Os");
   }

   ~CpuCapabilities()
   {
      _unmanagedLibrary.Dispose();
   }

   public bool CheckSse2Suppport()
   {
      _getCpuProperties();
      bool support = false;
      if ((_checkSse2Feature() == 1) & (_checkSse2Os() == 1))
      {
         support = true;
      }
      return support;
   }
}

The methods of CPUID.dll can be used from the CpuCapabilities class via delegate methods. When a new instance of the class is initialized, it gets all the methods from CPUID.dll.

The method CheckSse2Support is the most important of this class. It uses other methods of the class such as _checkSse2Feature and _checkSse2Os. If both of them support SSE2, then the method returns true; otherwise, it returns false. Returning false means that you can't use SSE2 support when generating random numbers. In addition, in the class, you have to call the _getCpuProperties method before calling the _checkSse2Feature or _checkSse2Os methods.

As I said before, the CpuCapabilities class uses the UnmanagedLibrary class to call the CPUID.dll methods. In OOP terms, the relationship between the CpuCapabilities class and the UnmanagedLibrary class is called composition. As you can see below, the composition icon denoting a containment relationship appears as an association with a filled diamond at the end denoting the aggregate. UnmanagedLibrary is a part of the CpuCapabilities class, and the lifecycle of the UnmanagedLibrary instance is managed by the lifecycle of the CpuCapabilities class. In other words, when a new instance of CpuCapabilities is created, a new instance of UnmanagedLibrary is created in the constructor of the CpuCapabilities class, and it's passed to the _unmanagedLibrary field of the CpuCapabilities class. Similarly, when the CpuCapabilities instance is destroyed, the UnmanagedLibrary instance (a field of the CpuCapabilities class) is destroyed automatically, too. For more information about composition, please visit this link.

PrngLib Project

PrngLib is my main project. To accomplish it, first of all, I opened my old SFMT solution (you can download it here) in Visual Studio 2008. Then, I clicked on it and added a new C# project to my SFMT solution using Add --> New project --> Visual C# --> Class Library commands, respectively. I renamed my new C# project to PrngLib. My PrngLib project will generate a DLL called PrngLib.dll, and this DLLl can be used by C# projects easily.

Again, I clicked PrngLib and added the UnmanagedLibrary class using Add-->Existing item-->UnmanagedLibrary.cs to the PrngLib project, and similarly added the CpuCapabilities class, too. Now, it's time to code for the SFMT classes in the project.

Strategy Design Pattern for SFMT

Design patterns are a must have for developing high-quality software. It's based on OO design, and today, they're in use with flying colors at the software industry.

In my PrngLib project, I'll use the Strategy pattern. It's in the Behavioral Patterns group, and it basically consists of decoupling an algorithm from its host, and encapsulating the algorithm into a separate class. More simply, an object and its behaviour are separated, and they're into two different classes. This allows you to switch the algorithm that you are using at any time. The general UML diagram is as shown in the figure:

In my side, this can give me the power of changing between the SFMTc and SFMTsse2 algorithms (DLLs) easily. In addition, I can add and use new algorithms, if they're introduced (may be new and faster SFMT algorithms in future).

SfmtStrategy Class

In the beginning, I added a new class which is named SfmtStrategy to my PrngLib project. This class is an abstract class and a kind of prototype to SFMT algorithms. You can see the whole code of this class below. As you can see, all the random number generation methods are +1 overloaded.

namespace PrngLib
{
    public abstract class SfmtStrategy
    {
        protected string _dllName = "";
        protected UnmanagedLibrary _unmanagedLibrary;

        ~SfmtStrategy()
        {
            _unmanagedLibrary.Dispose();
        }

        public abstract string DllName
        {
            get;
        }

        public abstract void MakeSeed();
        public abstract int Generate32BitInt();
        public abstract bool Generate32BitInt(int[] intArray);
        public abstract bool Generate32BitUInt(uint[] intArray);
        public abstract uint Generate32BitUInt();
        public abstract bool Generate64BitInt(long[] longArray);
        public abstract bool Generate64BitUInt(ulong[] longArray);
        public abstract long Generate64BitInt();
        public abstract ulong Generate64BitUInt();
    }
}
  • _dllName field: protected, and will be filled in derived classes.
  • _unmanagedLibrary field: protected, and will be initialized in derived classes.
  • DllName property: to get name of the DLL that is used by the current SfmtStrategy class instance.
  • MakeSeed() method: reference method to make a new SFMT period. It will be implemented in derived classes. Before making a new call to any generation method, you must call this method.
  • Generate32BitInt() methods: generates 32 bit integer and returns. The method will be implemented in derived classes.
  • Generate32BitInt(int[] intArray) method: generates 32 bit integers and fills the intArray parameter with these integers. The method will be implemented in derived classes.
  • Generate32BitUInt() method: generates a 32 bit unsigned integer and returns. It will be implemented in derived classes.
  • Generate32BitUInt(uint[] intArray) method: generates 32 bit unsigned integers and fills the intArray parameter with these integers. The method will be implemented in derived classes.
  • Generate64BitInt() method: generates a 64 bit integer and returns. It will be implemented in derived classes.
  • Generate64BitInt(long[] longArray) method: generates 64 bit integers and fills the longArray parameter with these integers. The method will be implemented in derived classes.
  • Generate64BitUInt() method: generates a 64 bit unsigned integer and returns. It will be implemented in derived classes.
  • Generate64BitUInt(ulong[] longArray) method: generates 64 bit unsigned integers and fills the longArray parameter with these integers. The method will be implemented in derived classes.

Now, it's time to add the derived classes and implement each abstract method in these classes. To achieve this, I added two new classes to my PrngLib project: SfmtC and SfmtSse2. These classes are inherited from the SfmtStrategy class, and both of them allow to implement their own algorithms via our C/C++ DLLs (by calling SfmtC.dll and Sfmtsse2.dll, of course).

SfmtC Class

This class implements the SfmtStrategy abstract class, and uses SfmtC.dll to generate integers. UnmanagedLibrary is a part of the SfmtC class and the relationship between these is composition. This class uses the SfmtC.dll code, and does not take advantage of hardware acceleration. The whole code is like this:

namespace PrngLib
{
public class SfmtC : SfmtStrategy
{

#region Delegate Functions
    delegate void init_gen_rand(uint seed);
    delegate uint gen_rand32();
    delegate ulong gen_rand64();
    delegate int fill_array32(int[] array, int size);
    delegate int fill_arrayU32(uint[] array, int size);
    delegate int fill_array64(long[] array, int size);
    delegate int fill_arrayU64(ulong[] array, int size);
#endregion

#region Fields
    init_gen_rand _initGenRand = null;
    gen_rand32 _genRand32 = null;
    gen_rand64 _genRand64 = null;
    fill_array32 _fillArray32 = null;
    fill_arrayU32 _fillArrayU32 = null;
    fill_array64 _fillArray64 = null;
    fill_arrayU64 _fillArrayU64 = null;
#endregion

#region Constructors
    public SfmtC()
    {
        _dllName = "SFMTc";
        _unmanagedLibrary = new UnmanagedLibrary(_dllName);
        _initGenRand = 
          _unmanagedLibrary.GetUnmanagedFunction<init_gen_rand>("init_gen_rand");
        _genRand32 = 
          _unmanagedLibrary.GetUnmanagedFunction<gen_rand32>("gen_rand32");
        _genRand64 = 
          _unmanagedLibrary.GetUnmanagedFunction<gen_rand64>("gen_rand64");
        _fillArray32 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_array32>("fill_array32");
        _fillArrayU32 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_arrayU32>("fill_array32");
        _fillArray64 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_array64>("fill_array64");
        _fillArrayU64 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_arrayU64>("fill_array64");
    }
#endregion

#region Properties
    public override string DllName
    {
        get { return _dllName; }
    }
#endregion

#region Methods
    public override void MakeSeed()
    {
        _initGenRand((uint)DateTime.Now.Millisecond);
    }

    public override int Generate32BitInt()
    {
        return (int)_genRand32();
    }

    public override bool Generate32BitInt(int[] intArray)
    {
        bool result = false;
        int r = _fillArray32(intArray, intArray.Length); 
        if (r == 1)
            result = true;
        return result;
    }

    public override bool Generate32BitUInt(uint[] intArray)
    {
        bool result = false;
        int r = _fillArrayU32(intArray, intArray.Length);
        if (r == 1)
        result = true;
        return result;
    }

    public override uint Generate32BitUInt()
    {
        return _genRand32();
    }

    public override bool Generate64BitInt(long[] longArray)
    {
        bool result = false;
        int r = _fillArray64(longArray, longArray.Length);
        if (r == 1)
            result = true;
        return result;
    }

    public override bool Generate64BitUInt(ulong[] longArray)
    {
        bool result = false;
        int r = _fillArrayU64(longArray, longArray.Length);
        if (r == 1)
            result = true;
        return result;
    }

    public override long Generate64BitInt()
    {
        return (long)_genRand64();
    }

    public override ulong Generate64BitUInt()
    {
        return _genRand64();
    }
#endregion

}
}

SfmtSse2 Class

Like the SfmtC class, this class implements the SfmtStrategy abstract class, and uses SfmtSse2.dll to generate integers. UnmanagedLibrary is a part of the SfmtSse2 class, and the relationship between these is composition. This class uses the SfmtSse2.dll code, and can take advantage of hardware acceleration. The whole code is like this:

namespace PrngLib
{
public class SfmtSse2 : SfmtStrategy
{

#region Delegate Functions
    delegate void init_gen_rand(uint seed);
    delegate uint gen_rand32();
    delegate ulong gen_rand64();
    delegate int fill_array32(int[] array, int size);
    delegate int fill_arrayU32(uint[] array, int size);
    delegate int fill_array64(long[] array, int size);
    delegate int fill_arrayU64(ulong[] array, int size);
#endregion

#region Fields
    init_gen_rand _initGenRand = null;
    gen_rand32 _genRand32 = null;
    gen_rand64 _genRand64 = null;
    fill_array32 _fillArray32 = null;
    fill_arrayU32 _fillArrayU32 = null;
    fill_array64 _fillArray64 = null;
    fill_arrayU64 _fillArrayU64 = null;
#endregion

#region Constructors
    public SfmtSse2()
    {
        _dllName = "SFMTsse2";
        _unmanagedLibrary = new UnmanagedLibrary(_dllName);
        _initGenRand = 
          _unmanagedLibrary.GetUnmanagedFunction<init_gen_rand>("init_gen_rand");
        _genRand32 = 
          _unmanagedLibrary.GetUnmanagedFunction<gen_rand32>("gen_rand32");
        _genRand64 =
           _unmanagedLibrary.GetUnmanagedFunction<gen_rand64>("gen_rand64");
        _fillArray32 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_array32>("fill_array32");
        _fillArrayU32 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_arrayU32>("fill_array32");
        _fillArray64 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_array64>("fill_array64");
        _fillArrayU64 = 
          _unmanagedLibrary.GetUnmanagedFunction<fill_arrayU64>("fill_array64");
    }
#endregion

#region Properties
    public override string DllName
    {
        get { return _dllName; }
    }
#endregion

#region Methods
    public override void MakeSeed()
    {
        _initGenRand((uint)DateTime.Now.Millisecond);
    }

    public override int Generate32BitInt()
    {
        return (int)_genRand32();
    }

    public override bool Generate32BitInt(int[] intArray)
    {
        bool result = false;
        int r = _fillArray32(intArray, intArray.Length);
        if (r == 1)
            result = true;
        return result;
    }

    public override bool Generate32BitUInt(uint[] intArray)
    {
        bool result = false;
        int r = _fillArrayU32(intArray, intArray.Length);
        if (r == 1)
            result = true;
        return result;
    }

    public override uint Generate32BitUInt()
    {
        return _genRand32();
    }

    public override bool Generate64BitInt(long[] longArray)
    {
        bool result = false;
        int r = _fillArray64(longArray, longArray.Length);
        if (r == 1)
            result = true;
        return result;
    }

    public override bool Generate64BitUInt(ulong[] longArray)
    {
        bool result = false;
        int r = _fillArrayU64(longArray, longArray.Length);
        if (r == 1)
            result = true;
        return result;
    }

    public override long Generate64BitInt()
    {
        return (long)_genRand64();
    }

    public override ulong Generate64BitUInt()
    {
        return _genRand64();
    }
#endregion

}
}

Below, you can see the class diagram and the relationship between the classes. Here, UnmanagedLibrary is a part of the SfmtC class, and the lifecycle of the UnmanagedLibrary instance is managed by the lifecycle of the SfmtC class. Also, this is the same for the SfmtSse2-UnmanagedLibrary classes, too. SfmtC and SfmtSse2 classes are inherited from SfmtStrategy, and in OOP terms, this kind of relationship is named Generalization (also known as an is a relationship). The generalization icon appears as an association with a closed arrowhead. The arrowhead points to the superclass, and the opposite end of the association designates the subclass. The SfmtStrategy class is the superclass, and its subclasses are SfmtC and SfmtSse2. For more information about Generalization, please visit this link.

Sfmt Class and its Members

The Sfmt class will be a front-end class, and C# applications will use this class and its methods to generate random integer numbers. To achieve this structure, first of all, I added a new C# abstract class, which is named Prng, to my PrngLib project. You can see the code below:

namespace PrngLib
{
    public abstract class Prng
    {
        protected Boolean _isNumberGenerated;

        /// <summary>
        /// It realize that the PRN is generated successfully
        /// </summary>
        public abstract Boolean IsNumberGenerated
        {
            get;
        }
        /// <summary>
        /// Generates seed for PRNGs.
        /// </summary>
        public abstract void MakeSeed();
    }
}
  • IsNumberGenerated: It indicates if the PRN was generated successfully.
  • MakeSeed: Generates a seed for PRNGs.

After that, I added a new C# class and renamed it to Sfmt. I made an is a relationship between the Prng class and the Sfmt class using the Visual Studio IDE class diagram, so the Sfmt class is derived from the Prng abstract class. As I said before, the Sfmt class is an important class in my project and I added some methods to it. Now, I'll try to explain this structure to you:

namespace PrngLib
{
    public class Sfmt : Prng
    {
#region Fields
        private const int _mexp = 19937;
        private SfmtStrategy _sfmtStrategy;
        private CpuCapabilities _cpuCapabilities; 
#endregion

#region Constructors
        /// <summary>
        /// Constructor.
        /// <para>After using this constructor, 
        /// you should call SetSfmtStrategy method</para> 
        /// </summary>
        public Sfmt()
        {
            this._cpuCapabilities = new CpuCapabilities();
            _isNumberGenerated = false;
        }

        /// <summary>
        /// Constructor.
        /// <summary> 
        /// <param name="sfmtStrategy">
        /// Object of a SfmtStrategy class.
        /// </param>
        public Sfmt(SfmtStrategy sfmtStrategy)
        : this()
        {
            SetSfmtStrategy(sfmtStrategy);
        } 
#endregion

#region Properties
        /// <summary>
        /// Mersenne Twister constant
        /// </summary>
        public int Mexp
        {
            get
            {
                return _mexp;
            }
        }    

        /// <summary>
        /// Realizes that the PRN is generated successfully
        /// </summary>
        public override bool IsNumberGenerated
        {
            get { return _isNumberGenerated; }
        }
#endregion

#region Methods
        /// <summary>
        /// Sets Sfmt number generation strategy.
        ///<para>Allows switching between Sfmt generation strategies.</para> 
        ///<para>Before using any Sfmt method that generate numbers 
        /// you must call it.</para> <para>Otherwise methods return 0.</para>
        /// </summary>
        /// <param name="sfmtStrategy">
        /// Object of a SfmtStrategy class.
        /// </param>
        public void SetSfmtStrategy(SfmtStrategy sfmtStrategy)
        {
            this._sfmtStrategy = sfmtStrategy;
        }

        /// <summary>
        /// Detects most efficient number generation strategy
        /// according to OS/CPU and sets it automatically.
        /// </summary>
        public void AutoSfmtStrategy()
        {
            if (_cpuCapabilities.CheckSse2Suppport())
                SetSfmtStrategy(new SfmtSse2());
            else
                SetSfmtStrategy(new SfmtC());
        }

        /// <summary>
        /// Generates seed for Sfmt.
        /// <para>You must call this method when trying
        /// to call any new number generation method</para> 
        /// </summary>
        public override void MakeSeed()
        {
            if (_sfmtStrategy != null)
            {
                _sfmtStrategy.MakeSeed();
            }
            _isNumberGenerated = false;
        }

        /// <summary>
        /// Generates and returns 32 bit signed integer.
        ///<para>If it fails then returns 0</para> 
        /// </summary>
        public int Generate32BitInt()
        {
            _isNumberGenerated = false;
            int number = 0;
            try
            { 
                if (_sfmtStrategy != null)
                {
                    number = _sfmtStrategy.Generate32BitInt();
                    _isNumberGenerated = true;
                }    
            }
            catch (Exception)
            {
                throw;
            }
            return number;
        }

        /// <summary>
        /// Generates and fills an array with 32 bit integers.
        ///<para>If it fails then returns false, otherwise returns true</para> 
        /// </summary>
        /// <param name="intArray">
        /// Array that will be filled with 32 bit integers.
        /// </param>
        public bool Generate32BitInt(int[] intArray)
        {
            _isNumberGenerated = false;
            try
            {
                if (_sfmtStrategy != null)
                {
                    if (_sfmtStrategy.Generate32BitInt(intArray))
                    {
                        _isNumberGenerated = true;
                    } 
                }
            }
            catch (Exception)
            {
                throw;
            }
            return _isNumberGenerated;
        }

        /// <summary>
        /// Generates and fills an array with 32 bit unsigned integers.
        ///<para>If it fails then returns false, otherwise returns true</para> 
        /// </summary>
        /// <param name="intArray">
        /// Array that will be filled with 32 bit unsigned integers.
        /// </param>
        public bool Generate32BitUInt(uint[] intArray)
        {
            _isNumberGenerated = false;
            try
            {
                if (_sfmtStrategy != null)
                {
                    if (_sfmtStrategy.Generate32BitUInt(intArray))
                    {
                        _isNumberGenerated = true;
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return _isNumberGenerated;
        }

        /// <summary>
        /// Generates and returns 32 bit unsigned integer.
        ///<para>If it fails then returns 0</para> 
        /// </summary>
        public uint Generate32BitUInt()
        {
            _isNumberGenerated = false;
            uint number = 0;
            try
            {
                if (_sfmtStrategy != null)
                {
                    number = _sfmtStrategy.Generate32BitUInt();
                    _isNumberGenerated = true; 
                }
            }
            catch (Exception)
            {
                throw;
            }
            return number;
        }

        /// <summary>
        /// Generates and fills an array with 64 bit integers.
        ///<para>If it fails then returns false, otherwise returns true</para> 
        /// </summary>
        /// <param name="longArray">
        /// Array that will be filled with 64 bit integers.
        /// </param>
        public bool Generate64BitInt(long[] longArray)
        {
            _isNumberGenerated = false;
            try
            {
                if (_sfmtStrategy != null)
                {
                    if (_sfmtStrategy.Generate64BitInt(longArray))
                    {
                        _isNumberGenerated = true;
                    } 
                }
            }
            catch (Exception)
            {
                throw;
            }
            return _isNumberGenerated;
        }

        /// <summary>
        /// Generates and fills an array with 64 bit unsigned integers.
        ///<para>If it fails then returns false, otherwise returns true</para> 
        /// </summary>
        /// <param name="longArray">
        /// Array that will be filled with 64 bit unsigned integers.
        /// </param>
        public bool Generate64BitUInt(ulong[] longArray)
        {
            _isNumberGenerated = false;
            try
            {
                if (_sfmtStrategy != null)
                {
                    if (_sfmtStrategy.Generate64BitUInt(longArray))
                    {
                        _isNumberGenerated = true;
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return _isNumberGenerated;
        }

        /// <summary>
        /// Generates and returns 64 bit signed integer.
        ///<para>If it fails then returns 0</para> 
        /// </summary>
        public long Generate64BitInt()
        {
            _isNumberGenerated = false;
            long number = 0;
            try
            {
                if (_sfmtStrategy != null)
                {
                    number = _sfmtStrategy.Generate64BitInt();
                    _isNumberGenerated = true;
                }
            }
            catch (Exception)
            {
                throw;
            }
            return number;
        }

        /// <summary>
        /// Generates and returns 64 bit unsigned integer.
        ///<para>If it fails then returns 0</para> 
        /// </summary>
        public ulong Generate64BitUInt()
        {
        _isNumberGenerated = false;
            ulong number = 0;
            try
            {
                if (_sfmtStrategy != null)
                {
                    number = _sfmtStrategy.Generate64BitUInt();
                    _isNumberGenerated = true; 
                }
            }
            catch (Exception)
            {
                throw;
            }
            return number;
        }
#endregion
    }
}

Members of the Sfmt class and their acts are listed below. I'll talk about them in the A Windows Forms Application: SfmtClient section.

  • _mexp field: it's a constant named Mersenne Twister exponent. It's equivalent to 19937.
  • _sfmtStrategy field: to manipulate the SfmtStrategy instance.
  • _cpuCapabilities field: to manipulate the CpuCapabilities instance.
  • Mexp property: to get the MEXP constant used by our Sfmt class.
  • Sfmt() constructor: default constructor. After using this constructor, you should call the SetSfmtStrategy method.
  • Sfmt(SfmtStrategy sfmtStrategy) constructor: Overloaded constructor.
  • SetSfmtStrategy(SfmtStrategy sfmtStrategy) method: to set and change the SFMT number generation strategy.
  • AutoSfmtStrategy() method: detects the most efficient number generation strategy according to OS/CPU, and sets it automatically.
  • IsNumberGenerated() method: to detect if the last attempt for generating a number succeeded.
  • MakeSeed() method: derived from the Prng class, and implements this method. You must call this method when trying to call any new number generation method.
  • Generate32BitInt() methods: generates a 32 bit integer and returns. It uses the sequential call concept of SFMT.
  • Generate32BitInt(int[] intArray) method: generates 32 bit integers, and fills the intArray parameter with these integers. It uses the block call concept of SFMT.
  • Generate32BitUInt() method: generates a 32 bit unsigned integer and returns. It uses the sequential call concept of SFMT.
  • Generate32BitUInt(uint[] intArray) method: generates 32 bit unsigned integers, and fills the intArray parameter with these integers. It uses the block call concept of SFMT.
  • Generate64BitInt() method: generates a 64 bit integer and returns. It uses the sequential call concept of SFMT.
  • Generate64BitInt(long[] longArray) method: generates 64 bit integers, and fills the longArray parameter with these integers. It uses the block call concept of SFMT.
  • Generate64BitUInt() method: generates a 64 bit unsigned integer and returns. It uses the sequential call concept of SFMT.
  • Generate64BitUInt(ulong[] longArray) method: generates 64 bit unsigned integers, and fills the longArray parameter with these integers. It uses the block call concept of SFMT.

In the screenshot below, you can see the Sfmt class and its relationship with other classes. Sfmt is derived from the Prng class (is a relationship = generalization). CpuCapabilities is part of Sfmt (part of relationship = composition). Sfmt has a SfmtStrategy, and in object oriented analysis and design, the relationship between SfmtStrategy and Sfmt is named aggregation (also known as a has a relationship). The aggregation icon appears as an association with an unfilled diamond at the end denoting the aggregate. In aggregation relationships, the lifecycle of the class instances are independent. SfmtStrategy is part of the Sfmt class, but the SfmtStrategy instance isn't managed by the lifecycle of the Sfmt class. Because, the SfmtStrategy instance must be created out of the Sfmt class, before an Sfmt instance, which will use the SfmtStrategy instance, is created. In other words, in aggregation, the object may only contain a reference or pointer to the other object. For more information, please visit this link.

Other term in object oriented methodology is multiplicity. Multiplicity specifies the number of classes that can be connected across an association (such as aggregation or composition). When determining multiplicity for an end of an association, ask yourself the question "How many instances of a class at this end may be associated with a single instance of the class at the other end"? Multiplicity symbols and their meanings are like this:

0..1 No instances, or one instance (optional)
1 Exactly one instance
0..* or * Zero or more instances
1..* One or more instances (at least one)

In my side, a SfmtStrategy instance may be associated with zero or more (* symbol) Sfmt instances. Also, a Sfmt class instance needs only one (as shown with an 1 symbol below) SfmtStrategy class instance. A CpuCapabilities instance must be associated with only one (1) Sfmt instance (because the CpuCapabilities instance lifecycle is managed by the Sfmt instance).

Whole Relationship Between Classes (UML) of PrngLib

Whole Visual Studio Class Diagram of the PrngLib Project

Now, after all of these, I built my PrngLib project and got a PrngLib.dll. This DLL can be used by C# applications easily. I had to write a Windows Forms application to test and use my new DLL.

A Windows Forms Application: SfmtClient

To get a Windows Forms application, first of all, I added a new project (Add --> New Project --> Visual C# --> Windows Forms Application) in my SFMT solution. After that, on my form, I added some components from the Toolbox and wrote some piece of code to test my SFMT library. Before running my application, I put all necessary DLLs (SfmtC.dll, SfmtSse2.dll, CPUID.dll, PrngLib.dll) to the same directory of SfmtClient. The screenshot is shown below and also my C# code:

using PrngLib;

namespace SFMTClient
{
    public partial class SfmtClientForm : Form
    {
        public PrngLib.Sfmt Sfmt;

        public SfmtClientForm()
        {
            InitializeComponent();
            Sfmt = new Sfmt();
            //also you can use --> new Sfmt(new PrngLib.SfmtC());
        }

        private void SfmtClientForm_Load(object sender, EventArgs e)
        {
            nud1.Value = 50000;
        }

        private void btnGenerate_Click(object sender, EventArgs e)
        {
            string result; 

            int count;
            if (cb1000Number.Checked)
                count = 1000;
            else
                count = Convert.ToInt32(nud1.Text);

            tb1.Hide();
            tb1.Text = "";
            this.Cursor = Cursors.WaitCursor;
            lblProcessing.Show();
            lblResult.Hide();
            Application.DoEvents();

            try
            {
                if (rbPureC.Checked)
                    Sfmt.SetSfmtStrategy(new PrngLib.SfmtC());
                else if (rbSSE2.Checked)
                    Sfmt.SetSfmtStrategy(new PrngLib.SfmtSse2());
                else if (rbAuto.Checked)
                    Sfmt.AutoSfmtStrategy();

                DateTime Anow = DateTime.Now;
                Sfmt.MakeSeed();//it's important to call MakeSeed();

                if ((rbSeparate.Checked))
                {
                    if (rb32i.Checked)
                        for (int i = 0; i < count; i++)
                            Sfmt.Generate32BitInt();
                    else
                        if (rb32ui.Checked)
                            for (int i = 0; i < count; i++)
                                Sfmt.Generate32BitUInt();
                    else
                        if (rb64i.Checked)
                            for (int i = 0; i < count; i++)
                                Sfmt.Generate64BitInt();
                    else
                        if (rb64ui.Checked)
                            for (int i = 0; i < count; i++)
                                Sfmt.Generate64BitUInt();
                }
                else
                    if ((rbFillingArray.Checked))
                    {
                        if (rb32i.Checked)
                        {
                            int[] myArray = new int[count];
                            Sfmt.Generate32BitInt(myArray);
                            if (cb1000Number.Checked)
                            {
                                foreach (int item in myArray)
                                  tb1.AppendText(item.ToString() + Environment.NewLine);
                                tb1.Show();
                            }
                        }
                        else
                            if (rb32ui.Checked)
                            {
                                uint[] myArray = new uint[count];
                                Sfmt.Generate32BitUInt(myArray);
                                if (cb1000Number.Checked)
                                {
                                    foreach (uint item in myArray)
                                      tb1.AppendText(item.ToString() + Environment.NewLine);
                                    tb1.Show();
                                }
                            }
                        else
                            if (rb64i.Checked)
                            {
                                long[] myArray = new long[count];
                                Sfmt.Generate64BitInt(myArray);
                                if (cb1000Number.Checked)
                                {
                                    foreach (long item in myArray)
                                      tb1.AppendText(item.ToString() + Environment.NewLine);
                                    tb1.Show();
                                }
                            }
                        else
                            if (rb64ui.Checked)
                            {
                                ulong[] myArray = new ulong[count];
                                Sfmt.Generate64BitUInt(myArray);
                                if (cb1000Number.Checked)
                                {
                                    foreach (ulong item in myArray)
                                      tb1.AppendText(item.ToString() + Environment.NewLine);
                                    tb1.Show();
                                }
                            }
                    }
                }
                DateTime Bnow = DateTime.Now;
                result = Convert.ToString(Bnow - Anow);
                lblResult.Text = count.ToString() + 
                   " count integers generated in " + result;
            }
            finally
            {
                this.Cursor = Cursors.Default;
                lblProcessing.Hide(); 
                lblResult.Show();
            }
        }

        private void cb1000Number_CheckedChanged(object sender, EventArgs e)
        {
            nud1.Enabled = !cb1000Number.Checked;
            gb3.Enabled = !cb1000Number.Checked;
            if (cb1000Number.Checked)
                rbFillingArray.Checked = true;
        }    
    }
}

As you can see, SfmtClient can use all the methods of the Sfmt class. You can generate 32 - 64 bit integers, and 32 - 64 bit unsigned integers easily. Also, you can choose the generation method (Separate integers or Filling array). Just by passing your array to the Sfmt methods, you can fill your array with integers easily. Don't forget that the filling array method is always much faster than the separate integers method. The filling array method uses and needs RAM. So, if you have a lot of available memory, you can generate more numbers using the filling array method. On the other hand, the separate integers method is slow, and this concept uses CPU power to generate numbers. In addition, you can determine a strategy. The available strategies are Pure C (in the background, this uses SfmtC.dll and C/C++ code), SSE2 support (in the background, this uses SfmtSse2.dll and the power of the CPU's instruction set), and Auto (detects the most efficient number generation strategy according to the OS/CPU and sets it automatically via Sfmt's AutoSfmtStrategy method). Don't forget that, if it's possible to use the SSE2 strategy on the machine, using this strategy is always faster than using the Pure C strategy. Below, you can see how the SFMT strategy can be easily changed on an instance of the Sfmt class:

if (rbPureC.Checked)
    Sfmt.SetSfmtStrategy(new PrngLib.SfmtC());
else if (rbSSE2.Checked)
    Sfmt.SetSfmtStrategy(new PrngLib.SfmtSse2());
else if (rbAuto.Checked)
    Sfmt.AutoSfmtStrategy();

Benefits of the SFMT Library and Important Notes

This new Sfmt library has some benefits and I'd like to mention them:

  1. Generating numbers is a must for statistical analysis and forecasting. Lots of Monte Carlo Simulations and similar problems need faster random number generation algorithms. The C# SFMT library enables you to add SFMT (SIMD-oriented Fast Mersenne Twister) power to your .NET applications.
  2. By coding just in C#, you can take advantage of the CPU's SSE2 support.
  3. Day by day, 64 bit support for applications is growing. The SFMT library can generate 64 bit numbers for you easily. In addition, as well as generating integer numbers, the SFMT library can generate unsigned integer numbers, too.
  4. Generation limitations of the SFMT library only depends on your machine limitations such as your RAM or the power of the CPU. Some SFMT implementations can't generate some integers. For example, rules like "array size must be a multiple of 4 or multiple of 2" and "array size must be greater than or equal to (MEXP / 128 + 1) * 4 or (MEXP / 128 + 1) * 2" are avoided to generate 1313 integers using the filing array method. But, generating a total 1313 integers is a piece of cake for the SFMT library.
  5. When working with the SFMT library, always use MakeSeed() before using any generation method or before changing between generation methods. This resets and clears other necessary things.
  6. The filling array methods are always faster than the separate integers methods. The filling methods are 20-35 times faster than the separate integers methods. Especially, using the SSE2 power with the filling array methods can give you approximately 35-40% performance gain compared to using Pure C with filling array methods. You can see the performance comparison test results which I have achieved on my machine, here:

Machine Configuration and Other Parameters:

  • Windows Vista Home Premium 32 bit
  • AMD Turion 64X2 Mobile Technology TL-60 2.00 GHz
  • 2046 MB RAM
  • Integer count that was generated in this test is 20.000.000

Results:

32 bit integers
Pure C
32 bit integers
SSE2 support
64 bit integers
Pure C
64 bit integers
SSE2 support
Separate integers 00:00:08.780 s 00:00:08.700 s 00:00:09.050 s 00:00:08.790 s
Filling array 00:00:00.343 s 00:00:00.250 s 00:00:00.650 s 00:00:00.450 s

Next Step

May be a Monte Carlo Simulation using C#.

Don't hesitate to ask me about this article and the SFMT library.

See you later.

References

History

  • October 31, 2009: First release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Emre Özgür İnce
Founder Parola Bilgi Teknolojileri Ltd.
Turkey Turkey
He was born in Bursa, Turkey in 1979, and still lives in this fascinating place.

He started programming with Fortran when he was at university and then has experienced with MS Visual Basic, Borland Delphi, Java, C++ and C#.
 
He developed database based several client-server and multi-tier applications using OO concepts and OOP. He worked with Oracle, MySQL, MSSQL and Firebird RDBMS.
 
In the past, he worked on freetime image processing projects using OpenGL, DirectX, SDL technologies. He's interested in encryption/decryption and compression/decompression algorithms, random number generators and computational algorithms such as Monte Carlo Methods.
 
In 2011, he founded Parola Information Technology Ltd.
 
http://www.markanabak.com.tr is serviced by Parola Information Technology Ltd. It's a SAAS model for Intellectual Property sector.

Comments and Discussions

 
Questionusage in 64 bit environment [modified] PinmemberGerard van der Haven27-Feb-13 21:49 
QuestionDemo/Source doesn't load PinmemberSabresite25-Mar-12 22:55 
QuestiondSFMT? Please!? PinmemberSabresite25-Mar-12 20:41 
GeneralWhy not use C++/CLI Pinmemberviewon014-May-10 1:47 
GeneralRe: Why not use C++/CLI PinmemberEmre Özgür İnce5-Jun-10 10:01 
QuestiondSFMT is next? PinmemberLampiao15-Jan-10 7:20 
AnswerRe: dSFMT is next? PinmemberEmre Özgür İnce19-Jan-10 20:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 31 Oct 2009
Article Copyright 2009 by Emre Özgür İnce
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid