Intel® oneAPI Math Kernel Library
Ask questions and share information with other developers who use Intel® Math Kernel Library.

Microsoft Visual C# and Intel MKL

m_kozlov1
Beginner
1,548 Views
Hi All!
Is there any example how to call Intel MKL from C#?
Thanks in advance
0 Kudos
10 Replies
havard_vold
Beginner
1,548 Views

Ditto, I have been successful with IPP and C# (where I was led by some kind person to samples), but not yet with MKL.

There seems to be no downloadable samples for MKL?

Hvard

0 Kudos
Todd_R_Intel
Employee
1,548 Views
Currently, there is only the Fortran and C sample code that is included with the MKL product in the examples directory. There is no C# sample source code. There are no separately downloadable samples at this point either.
Has anyone used MKL in a simple C# program? Please attach your source code?
Todd
0 Kudos
Intel_C_Intel
Employee
1,548 Views
We are using P/Invoke to write a wrapper around some of the BLAS and LAPACK routines in MKL. Since P/Invoke requires a DLL, we couldn't call the MKL lib files directly. We wrote a simple wrapper DLL that we make P/Invoke calls to. You can get the source from
http://www.dnanalytics.net/, it might give you some
ideas.

Feel free to email at marcus@cuda.org if you have any questions.
0 Kudos
Trevor_Misfeldt
Beginner
1,548 Views
Currently, there is only the Fortran and C sample code that is included with the MKL product in the examples directory. There is no C# sample source code. There are no separately downloadable samples at this point either.
Has anyone used MKL in a simple C# program? Please attach your source code?
Todd

Here's how we do at CenterSpace Software. The data blocks are C# wrappers of double arrays. The offset is into the array. Basically, you statically link the mkl libraries, pin a pointer to your double array and call MKL.

- Trevor

#using

#using

#include // for memset.

#include "mkl.h"

# define BLAS_PREFIX(x) x
# define BLAS_ARGUMENT(x) &##x
# define INTEGER int

using namespace CenterSpace::NMath::Core;

namespace CenterSpace
{
namespace NMath
{
namespace Kernel
{
public ref class DotNetBlas
{
public:

// Deep copy of vector
static void copy( INTEGER n, FloatDataBlock x, INTEGER xOffset, INTEGER incx, FloatDataBlock y, INTEGER yOffset, INTEGER incy )
{
pin_ptr xptr;
pin_ptr yptr;

if ( n > 0 ) {
xOffset += AdjustVectorOffset( n, incx, x.Offset );
yOffset += AdjustVectorOffset( n, incy, y.Offset );
xptr = &( x.Data[xOffset] );
yptr = &( y.Data[yOffset] );
BLAS_PREFIX(scopy)( BLAS_ARGUMENT(n), xptr, BLAS_ARGUMENT(incx), yptr, BLAS_ARGUMENT(incy) );
}
}
}
}
}

0 Kudos
Todd_R_Intel
Employee
1,548 Views

We've posted some example C# programs that call Intel MKL.

Let us know if there are improvements we can make to further show how to access the features of Intel MKL.

Todd
0 Kudos
dpbsmith
Beginner
1,548 Views

OK, I've gotten it to work in a C# and Visual Studio IDE environment. (I was hung up for a while on getting the PATH environment variable set properly).

I'd like to know the reason why you create a separate class and "wrapper" functions. That is, in your example, you have access structured like this:


public sealed class DFTI
{

/** DFTI DftiCreateDescriptor wrapper */

public static int DftiCreateDescriptor(ref IntPtr desc,
int precision, int domain, int dimention, int length)
{
return DFTINative.DftiCreateDescriptor(ref desc,
precision, domain, dimention, length);
}

...

[SuppressUnmanagedCodeSecurity]
internal sealed class DFTINative
{
/** DFTI native DftiCreateDescriptor declaration */
[DllImport("mkl.dll", CallingConvention=CallingConvention.Cdecl,
ExactSpelling=true, SetLastError=false)]
internal static extern int DftiCreateDescriptor(ref IntPtr desc,
int precision, int domain, int dimention, int length);

Since the wrapper functions simply pass their arguments through unchanged, what is the benefit of the wrapper functions? Why is it better for the parts of the code that use this function to DFTI.DftiCreateDescriptor, rather than calling DFTINative.DftiCreateDescriptor directly themselves?

I assume it has something or other to do with containment of unsafe code, but I'd like to understand it better... and understand whether it is truly making the code safer.

0 Kudos
Kai_S_
Beginner
1,548 Views

Here is one piece of sample code you can use. "mkl_custom.dll" is a custom build mkl lib.

#region PInvoke calls
// CBLAS
[DllImport(@"mkl_custom.dll", ExactSpelling = true, SetLastError = false,
CallingConvention = CallingConvention.Cdecl)]
internal static extern void cblas_dcopy(int n, [In] double[] x, int incX,
[In, Out] double[] y, int incY);

#endregion PInvoke calls

0 Kudos
VipinKumar_E_Intel
1,548 Views
0 Kudos
digichip
Beginner
1,548 Views

We are using P/Invoke to write a wrapper around some of the BLAS and LAPACK routines in MKL. Since P/Invoke requires a DLL, we couldn't call the MKL lib files directly. We wrote a simple wrapper DLL that we make P/Invoke calls to. You can get the source from
https://www.digichip.in/, it might give you some
ideas.

Feel free to email at marcus@cuda.org if you have any questions.

0 Kudos
Reinot__Tonu
Beginner
1,543 Views

I essentially have written wrappers for all functions that have been interest me.  One such minimalistic wrapper is in the class VSL below.  Wrapper is pure C#, the main application is in Windows Forms paradigm.  There I created a form with three buttons and a RichTextView to see the output.

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Lognorm_bug
{
public partial class Form1 : Form
{
int i, len = 1000;
float[] rnd;
float div = 0.42466090014401f; // div=1/(2sqrt(2ln(2)))
uint seed;
IntPtr stream;
int status;
public Form1()
{
InitializeComponent();
seed = (uint)(DateTime.Now.Ticks / 10000L);
stream = new IntPtr();
status = VSL.vslNewStream(ref stream, VSL.BRNG.MT19937, seed);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
VSL.vslDeleteStream(ref stream);
}
private void button0_Click(object sender, EventArgs e)
{
IntPtr stream = new IntPtr();
rnd = new float[len];
status = VSL.vslNewStream(ref stream, VSL.BRNG.MT19937, seed);
status = VSL.vsRngGaussian(VSL.RNG_METHOD.GAUSSIAN_BOXMULLER, stream, len, rnd, 20f, 1f);
status = VSL.vsRngWeibull(VSL.RNG_METHOD.WEIBULL_ICDF, stream, len, rnd, 3f, 0f, 20f);
// Lognormal generator has a bug. All numbers need to be divided by 10,000,000 and the displacement (b) needs to be added after the call
status = VSL.vsRngLognormal(VSL.RNG_METHOD.LOGNORMAL_ICDF, stream, len, rnd, 20f, 0.8f, 0f, 1f);
VSL.vslDeleteStream(ref stream);
}

private void button1_Click(object sender, EventArgs e)
{
rnd = new float[len];
status = VSL.vsRngGaussian(VSL.RNG_METHOD.GAUSSIAN_BOXMULLER, stream, len, rnd, 20f, 1f * div);
Print();
}

private void button2_Click(object sender, EventArgs e)
{
rnd = new float[len];
status = VSL.vsRngWeibull(VSL.RNG_METHOD.WEIBULL_ICDF, stream, len, rnd, 3f, 0f, 20f);
Print();
}


private void button3_Click(object sender, EventArgs e)
{
rnd = new float[len];
status = VSL.vsRngLognormal(VSL.RNG_METHOD.LOGNORMAL_ICDF, stream, len, rnd, 20f, 0.8f, 0f, 1f);
Print();
}
private void Print()
{
rxView.Clear();
for (i = 0; i < 20; i++)
rxView.AppendText(rnd[i].ToString("F1") + "\n");
}
}

public sealed class VSL
{
private VSL() { }
#region Constants
public sealed class BRNG
{
public static int INC = 1 << 20;
public static int MCG31 = INC;
public static int R250 = (MCG31 + INC);
public static int MRG32K3A = (R250 + INC);
public static int MCG59 = (MRG32K3A + INC);
public static int WH = (MCG59 + INC);
public static int SOBOL = (WH + INC);
public static int NIEDERR = (SOBOL + INC);
public static int MT19937 = (NIEDERR + INC);
}
public sealed class RNG_METHOD
{
public static int ACCURACY_FLAG = (1 << 30);
public static int GAUSSIAN_BOXMULLER = 0;
public static int WEIBULL_ICDF = 0;
public static int LOGNORMAL_BOXMULLER2 = 0;
public static int LOGNORMAL_ICDF = 1;
public static int LOGNORMAL_BOXMULLER2_ACCURATE = LOGNORMAL_BOXMULLER2 | ACCURACY_FLAG;
public static int LOGNORMAL_ICDF_ACCURATE = LOGNORMAL_ICDF | ACCURACY_FLAG;
}
#endregion
#region Random number generator wrappers
const string libName = "mkl_rt.dll";
[DllImport(libName, EntryPoint = "vslNewStream", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = true)]
public static extern int vslNewStream(ref IntPtr stream, int brng, uint seed);
[DllImport(libName, EntryPoint = "vslDeleteStream", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = true)]
public static extern int vslDeleteStream(ref IntPtr stream);
[DllImport(libName, EntryPoint = "vsRngGaussian", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = true)]
public static extern int vsRngGaussian(int method, IntPtr stream, int count, [Out]float[] r, float mean, float stdev);

[DllImport(libName, EntryPoint = "vsRngWeibull", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = true)]
public static extern int vsRngWeibull(int method, IntPtr stream, int count, [Out]float[] r, float alpha, float a, float beta);

[DllImport(libName, EntryPoint = "vsRngLognormal", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, SetLastError = true)]
public static extern int vsRngLognormal(int method, IntPtr stream, int count, [Out]float[] r, float a, float sigma, float b, float beta);
#endregion
}
}

 

0 Kudos
Reply