ATI Stream SDK v2.0がリリースされ、OpenCLが使用できるようになったので試しにSQLServerのデータをGPUで処理してみた。
その具体的方法を紹介する。


方法はオーソドックスである。
データは次のように流れる。


SQLServer->DLL1->DLL2->GPU(AMD RADEON5750)。


DLL1はC#で作成されるMicrosoft.SqlServer.Server.SqlProcedureをプロパティにもつクラス。
このDLLはストアドとして、SQLServerから呼ばれ、入力データを作成し、DLL2へ投げる。
DLL2はC++で作成され、もらったデータを更に変換しGPUを投げて結果を受け取り、DLL1へ返す。
DLL1は結果データをSQLServerのテーブルをUPDATEする。


DLL1はVisualStudio2008のSQLServerプロジェクトである。unsafe有効, AnyCPUでビルドしている。
DLL2はATI Stream SDK v2.0中のサンプルであるtemplateCとほぼ同じである。main(int argc, char * argv[])がほぼDLLEXPORTになっただけである。
x64でビルドした。(使用しているPCがWin7 64bitなので)


処理時間はTSQLと比べて、1分以上の処理が1秒以内になったので、ざっと数十倍から数百倍のスピードアップであろう。


OpenCLC言語仮想マシン上で動くスクリプトのようである。
[DllImport("dll2")]するとDLL2をSQLServerがつかんで離さないので、Loadlibrary,Freelibraryで動的にロードした。
メモリの操作を誤ると、SQLServerが落ちてしまう。


DLL1は以下。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Runtime.InteropServices;

public partial class StoredProcedures
{
[DllImport("kernel32")]
private extern static System.IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32")]
private extern static bool FreeLibrary(System.IntPtr hLibModule);
[DllImport("kernel32")]
private extern static System.IntPtr GetProcAddress(System.IntPtr hModule, string lpProcName);

unsafe delegate System.Int64 Calc3OpenCl( float* ps, System.Int64 xsize, System.Int64 ysize );

[Microsoft.SqlServer.Server.SqlProcedure]
public static void sp_csCalc1()
{
try
{
using(SqlConnection con = new SqlConnection("context connection=true"))
{
con.Open();

string sql = "SELECT * FROM OPENCL_DATA ORDER BY IDX";
DataTable tbl = new DataTable();

SqlDataAdapter ad = new SqlDataAdapter( sql, con );
ad.Fill( tbl );

/* go */
Calc( tbl );


/* table update */
SqlCommandBuilder bld = new SqlCommandBuilder( ad );
ad.Update( tbl );

SqlContext.Pipe.Send( "OPENCL SUCCESS" );
}
}
catch( SqlException ex )
{
SqlContext.Pipe.Send( "SqlException:" + ex.Message );
}
catch( Exception ex )
{
SqlContext.Pipe.Send( "Exception:" + ex.Message );
}

}
private static unsafe void Calc( DataTable tbl )
{
int rows = tbl.Rows.Count;
int cols = tbl.Columns.Count;

float [,] arr = new float[rows,cols];


/* set input value*/
for( int iy = 0; iy < rows; iy++ )
{
/* ix=0:index */
arr[iy,1] = (float)tbl.Rows[iy][1];
}

/* calculation on opencl HD5750 */
long r = CalcOpenCl( arr, cols, rows );

if ( r != 0 )
throw new Exception( "opencl error:" + r.ToString());


/* get output value*/
for( int iy = 0; iy < rows; iy++ )
{
/* ix=0:index, ix=1:input value */
for( int ix = 2; ix < cols; ix++ )
tbl.Rows[iy][ix] = arr[iy,ix];
}
}
const string fnm = @"E:\work_vs\TestSqlclr1\debug\Debug\opencl_exchange.dll";

private static unsafe long CalcOpenCl( float[,] arr, int cols, int rows )
{
System.IntPtr hmod = LoadLibrary(fnm);

if ( hmod.ToInt64() == 0 )
throw new Exception( " not found:" + fnm );

long r = 0;
try
{
System.IntPtr funcaddr = GetProcAddress( hmod, "Calc3OpenCl");
Calc3OpenCl func = (Calc3OpenCl)Marshal.GetDelegateForFunctionPointer( funcaddr, typeof(Calc3OpenCl));


fixed( float* parr = arr )
{
r = func( parr, cols, rows );
}
}
finally
{
FreeLibrary( hmod );
}

return r;
}


};

DLL2は以下。


DLLEXPORT __int64 WINAPI Calc3OpenCl( float* psa, const __int64 xsize, const __int64 ysize )
{
try
{
return main( psa,xsize, ysize); // 中はtemplateCとほぼ同じなので、略。
}
catch(...)
{
return -1;
}
}