Wednesday, July 31, 2013

Java Uygulamalarından OpenCL kütüphanesine Erişim

   Uzun bir süredir, geliştirdiğimiz uygulamalar çok çekirdekli sistemler üzerinde çalışıyor. Ancak uygulamaları hala sanki tek işlemcili bir sistem üzerinde çalışacakmış gibi yazıyoruz. Bu durumda çok çekirdekli yapının bilgi işleme kapasitesinden yararlanamamış oluyoruz ve uygulamamız tek bir çekirdeği kullandığı için cevap süresi yüksek kalıyor. Artık bir çok platform çok çekirdekli mimariler üzerinde uygulama geliştirmeyi kolaylaştırmak için çözümler sunuyor. Java SE 6 platformunda Concurrency API'yi kullanarak çok iş parçacıklı uygulamalar geliştirmek mümkün olsa da Java SE'deki diğer standart API'ler (örneğin Collections, Networking, Security API) çok çekirdekli mimari göz önüne alınarak gerçeklenmedikleri için yazılımın çalışma zamanındaki başarımı kısıtlı ölçüde artış göstermektedir. Bu durum Java 7'deki Fork-Join  ve özellikle 2014 yılında çıkması beklenen JDK 8'deki Paralel Torbalar ve Dönüştür/İndirge çatısı ile aşılacakmış gibi görünüyor. Diğer taraftan çok çekirdekli mimariler işlemciler ile kısıtlı değiller, örneğin grafik işlemciler de çok çekirdekli mimariye sahipler. Üstelik grafik işlemcilerde genel amaçlı işlemcilere göre çok daha fazla sayıda çekirdek bulunmaktadır. Java platformunda GPU programlamayı Java 9 ile birlikte gerçekleştiriyor olabiliriz.
   OpenCL kütüphanesi hem merkezi işlemci birimi hem de grafik işlemciler üzerinde işlemcinin üreticisinden, mimarisinden ve türünden bağımsız olarak uygulama geliştirmemize olanak sağlayan bir çözüm olarak öne çıkmaktadır. OpenCL Khoronos Group tarafından geliştirilmektedir. Şu an 1.2 sürümü mevcuttur. OpenCL'de temel programlama dili C'dir. Java'dan OpenCL yordamlarına Java Native Interface (JNI) aracılığı ile erişim mümkündür. Bu yazıda OpenCL yordamlarına Java sınıflarından nasıl erişilebileceği konusu anlatılmaktadır. 
   OpenCL kütüphanesine Java platformundan JNI ile erişeceğiz. Bunun için OpenCL platformlarının listesini ve cihaz bilgilerini almak için PlatformInfo ve DeviceInfo sınıflarını kullanacağız:
PlatformInfo.java
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.example.opencl.utility;

import java.io.Serializable;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class PlatformInfo implements Serializable {

    private String platformVendor;
    private int numberOfDevices;
    private DeviceInfo[] deviceInfos;

    public PlatformInfo(String platformVendor, int numberOfDevices) {
        this.platformVendor = platformVendor;
        this.numberOfDevices = numberOfDevices;
        deviceInfos = new DeviceInfo[numberOfDevices];
    }

    public String getPlatformVendor() {
        return platformVendor;
    }

    public int getNumberOfDevices() {
        return numberOfDevices;
    }

    public DeviceInfo getDeviceInfo(int deviceNo) {
        return deviceInfos[deviceNo];
    }

    public void setDeviceInfo(int deviceNo, DeviceInfo deviceInfo) {
        this.deviceInfos[deviceNo] = deviceInfo;
    }

    public DeviceInfo[] getDeviceInfos() {
        return deviceInfos;
    }

    @Override
    public String toString() {
        return "PlatformInfo{" + "platformVendor=" + platformVendor + ", numberOfDevices=" + numberOfDevices + '}';
    }
    
}
DeviceInfo.java
 2
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.example.opencl.utility;

import java.io.Serializable;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class DeviceInfo implements Serializable {
      private int addressSize;
      private String extensions;
      private String deviceName;

    public DeviceInfo(int addressSize, String extensions, String deviceName) {
        this.addressSize = addressSize;
        this.extensions = extensions;
        this.deviceName = deviceName;
    }

    public int getAddressSize() {
        return addressSize;
    }

    public void setAddressSize(int addressSize) {
        this.addressSize = addressSize;
    }

    public String getExtensions() {
        return extensions;
    }

    public void setExtensions(String extensions) {
        this.extensions = extensions;
    }

    public String getDeviceName() {
        return deviceName;
    }

    public void setDeviceName(String deviceName) {
        this.deviceName = deviceName;
    }

    @Override
    public String toString() {
        return "DeviceInfo{" + "deviceName=" + deviceName + ", addressSize=" + addressSize + ", \nextensions=" + extensions + "\n}";
    }
      
}
OpenCL ile bağlantıyı aşağıda verilen OpenCLUtility sınıfı ile kuracağız:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.example.opencl.utility;

import com.example.domain.Element;

/**
 *
 * @author Binnur Kurt (binnur.kurt@gmail.com)
 */
public class OpenCLUtility {
      public native PlatformInfo[] getPlatformInfo();
      public native void execute(Element[] array);
      static {
         System.loadLibrary("myopencllib"); 
      }    
}
JNI için gerekli C++ başlık dosyasını aşağıdaki komutu kullanarak üretiyoruz:
javah -jni -o OpenCLUtility.h com.example.opencl.utility.OpenCLUtility
OpenCLUtility.h
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <jni.h>
/* Header for class com_example_opencl_utility_OpenCLUtility */

#ifndef _Included_com_example_opencl_utility_OpenCLUtility
#define _Included_com_example_opencl_utility_OpenCLUtility
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_opencl_utility_OpenCLUtility
 * Method:    getPlatformInfo
 * Signature: ()[Lcom/example/opencl/utility/PlatformInfo;
 */
JNIEXPORT jobjectArray JNICALL Java_com_example_opencl_utility_OpenCLUtility_getPlatformInfo
  (JNIEnv *, jobject);

/*
 * Class:     com_example_opencl_utility_OpenCLUtility
 * Method:    execute
 * Signature: ([Lcom/example/domain/Element;)V
 */
JNIEXPORT void JNICALL Java_com_example_opencl_utility_OpenCLUtility_execute
  (JNIEnv *, jobject, jobjectArray);

#ifdef __cplusplus
}
#endif
#endif
OpenCLUtility.cpp:
1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

#include "OpenCLUtility.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>

#include <CL/cl.h>

#define LOCAL_SIZE 512

cl_mem  g_inputBuffer = NULL;
cl_context g_context = NULL;
cl_command_queue g_cmd_queue = NULL;
cl_program g_program = NULL;
cl_kernel g_kernel = NULL;

#include "OpenCLUtil.h"

//for perf. counters
#include <Windows.h>

LARGE_INTEGER g_PerfFrequency;
LARGE_INTEGER g_PerformanceCountNDRangeStart;
LARGE_INTEGER g_PerformanceCountNDRangeStop;

struct JobInfo {
    char* companyName;
    char* companyAddress;
    char* telNumber;
    char* faxNumber;
} ;

typedef struct JobInfo TJobInfo;
typedef struct JobInfo* PJobInfo;

struct Element {
    int id;
    int identityCardNo;
    char* name;
    char* surname;
    int age;
    TJobInfo jobInfo;
    char* email;
    bool selected;
    bool masked;
} ;

typedef struct Element TElement;
typedef struct Element* PElement;

JNIEXPORT jobjectArray JNICALL Java_com_example_opencl_utility_OpenCLUtility_getPlatformInfo
(JNIEnv *env, jobject obj){
      
 /* Host/device data structures */
 cl_platform_id *platforms;
 cl_uint numberOfPlatformsAvailable;
 cl_device_id *devices;
 cl_uint num_devices, addr_data;
 cl_int i,j, err;

 /* Extension data */
 char name_data[48], ext_data[4096];
 char platform_vendor[2][40]; 

 /* Identify a platform */
 err = clGetPlatformIDs(10, NULL, &numberOfPlatformsAvailable);   
 if (err < 0) {   
    perror("Error: Couldn't find any platforms");
    exit(1);
 }
 platforms= (cl_platform_id*)malloc(sizeof(cl_platform_id)*numberOfPlatformsAvailable);
 err = clGetPlatformIDs(numberOfPlatformsAvailable, platforms, NULL);   
 if (err < 0) {   
       perror("Couldn't find any platforms");
      exit(1);
 }
 printf("There are %d platforms available in the system.\n",numberOfPlatformsAvailable);
 /*
        Create the result array of PlatformInfo
 */
 jclass platformInfoClass= env->FindClass("com/example/opencl/utility/PlatformInfo");
        jclass deviceInfoClass= env->FindClass("com/example/opencl/utility/DeviceInfo");
        jmethodID platformInfoClassConstructorId= env->GetMethodID(platformInfoClass,"<init>","(Ljava/lang/String;I)V");
        jmethodID deviceInfoClassConstructorId= env->GetMethodID(deviceInfoClass,"<init>","(ILjava/lang/String;Ljava/lang/String;)V");
        jmethodID platformInfoSetDeviceInfoId= env->GetMethodID(platformInfoClass,"setDeviceInfo","(ILcom/example/opencl/utility/DeviceInfo;)V");

        //jobjectArray result= env->NewObjectArray(platformInfoClass,platformInfoClassConstructorId,vendorName,numberOfPlatformsAvailable);
        jobjectArray result= env->NewObjectArray(numberOfPlatformsAvailable,platformInfoClass,NULL);
       
 for (j=0;j<numberOfPlatformsAvailable;++j){
      /* Determine number of connected devices */
      err = clGetDeviceIDs(platforms[j], CL_DEVICE_TYPE_ALL, 1, NULL, &num_devices);    
      if(err < 0) {    
     perror("Error: Couldn't find any devices.");
    exit(1);
      }
             clGetPlatformInfo(platforms[j],CL_PLATFORM_VENDOR,sizeof(platform_vendor),&platform_vendor[j],NULL);
     // Access connected devices
     devices = (cl_device_id*) malloc(sizeof(cl_device_id) * num_devices);  
     clGetDeviceIDs(platforms[j], CL_DEVICE_TYPE_ALL,num_devices, devices, NULL);    

     // Create PlatformInfo and set to the result array
     jstring  vendorName= env->NewStringUTF(platform_vendor[j]);
          jobject platformObject= env->NewObject(platformInfoClass,platformInfoClassConstructorId,vendorName,num_devices);
     env->SetObjectArrayElement(result,j,platformObject);  

     // Obtain data for each connected device
     for (i=0; i<num_devices; i++) {
        
     err = clGetDeviceInfo(devices[i], CL_DEVICE_NAME,sizeof(name_data), name_data, NULL);   
     if (err < 0) {  
      perror("Error: Couldn't read extension data.");
     exit(1);
     }
     clGetDeviceInfo(devices[i], CL_DEVICE_ADDRESS_BITS,sizeof(ext_data), &addr_data, NULL);   
     clGetDeviceInfo(devices[i], CL_DEVICE_EXTENSIONS, sizeof(ext_data), ext_data, NULL);   
     // create device info and set it to the platform
     jstring extensions= env->NewStringUTF(ext_data);
     jstring deviceName= env->NewStringUTF(name_data);
     jobject deviceObject= env->NewObject(deviceInfoClass,deviceInfoClassConstructorId,addr_data,extensions,deviceName);
              env->CallDoubleMethod(platformObject,platformInfoSetDeviceInfoId,i,deviceObject);
     }       
     free(devices);
    }       
    // free data structures
    free(platforms);
    return result;
}

char* jstringToCString(JNIEnv *env,jstring msg){
   const char* cstringTemp;
   cstringTemp= env->GetStringUTFChars(msg,NULL);
   int len= env->GetStringLength(msg)+1;
   char *resCString= (char*)malloc(sizeof(char)*len);
   strcpy(resCString,cstringTemp);
   *(resCString+len-1)='\0';
   env->ReleaseStringUTFChars(msg,cstringTemp);
   return resCString;
}

char* jstringToCStringV2(JNIEnv *env,jstring msg){
   return const_cast<char*>(env->GetStringUTFChars(msg,NULL));
}

void JniConvertToNativeArrayToElementArray(JNIEnv *env, jobject *obj, PElement nativeArray,jobjectArray *arr){
   int arraySize= env->GetArrayLength(*arr);
   jclass elementClass= env->FindClass("com/example/domain/Element");
   jclass javaUtilListClass= env->FindClass ("Ljava/util/ArrayList;");
   jclass jobInfoClass= env->FindClass("com/example/model/JobInfo");
   // Field IDs
   jfieldID idFieldID= env->GetFieldID(elementClass,"id","I");
   jfieldID ageFieldID= env->GetFieldID(elementClass,"age","I");
   jfieldID identityCardNoFieldID= env->GetFieldID(elementClass,"identityCardNo","I");

      for (int i=0;i<arraySize;++i){
       jobject jniArrayElement= env->GetObjectArrayElement(*arr,i);             
       env->SetIntField(jniArrayElement,ageFieldID,nativeArray[i].age);
       env->SetIntField(jniArrayElement,identityCardNoFieldID,nativeArray[i].identityCardNo);
       env->SetIntField(jniArrayElement,idFieldID,nativeArray[i].id);    
      }
}

PElement JniConvertElementArrayToNative(JNIEnv *env, jobject *obj, jobjectArray *arr){
   int arraySize= env->GetArrayLength(*arr);
   PElement nativeArray= (PElement) malloc(sizeof(TElement) * arraySize);
   jclass elementClass= env->FindClass("com/example/domain/Element");
   jclass javaUtilListClass= env->FindClass ("Ljava/util/ArrayList;");
   jclass jobInfoClass= env->FindClass("com/example/model/JobInfo");
   // Field IDs
      jfieldID idFieldID= env->GetFieldID(elementClass,"id","I");
      jfieldID ageFieldID= env->GetFieldID(elementClass,"age","I");
      jfieldID nameFieldID= env->GetFieldID(elementClass,"name","Ljava/lang/String;");
      jfieldID surnameFieldID= env->GetFieldID(elementClass,"surname","Ljava/lang/String;");
   jfieldID emailFieldID= env->GetFieldID(elementClass,"email","Ljava/lang/String;");
      jfieldID identityCardNoFieldID= env->GetFieldID(elementClass,"identityCardNo","I");
      jfieldID jobInfoFieldID= env->GetFieldID(elementClass,"jobInfo","Lcom/example/model/JobInfo;");
      jfieldID companyNameFieldID= env->GetFieldID(jobInfoClass,"companyName","Ljava/lang/String;"); 
      jfieldID companyAddressFieldID= env->GetFieldID(jobInfoClass,"companyAddress","Ljava/lang/String;");
      jfieldID telNumberFieldID= env->GetFieldID(jobInfoClass,"telNumber","Ljava/lang/String;");
      jfieldID faxNumberFieldID= env->GetFieldID(jobInfoClass,"faxNumber","Ljava/lang/String;");
      // Local fields
      jstring jstringName;
      jstring jstringSurname;
      jstring jstringEmail;
      jstring jstringCompanyName;
      jstring jstringCompanyAddress;
      jobject jobInfo;
      jstring telNumber;
      jstring faxNumber;
      int listSize;

   for (int i=0;i<arraySize;++i){
    jobject jniArrayElement= env->GetObjectArrayElement(*arr,i);          
       nativeArray[i].age= (jint) env->GetIntField(jniArrayElement,ageFieldID);
       nativeArray[i].id= (jint) env->GetIntField(jniArrayElement,idFieldID);    
       nativeArray[i].identityCardNo= (jint) env->GetIntField(jniArrayElement,identityCardNoFieldID);
    jstringName= (jstring) env->GetObjectField(jniArrayElement,nameFieldID);
    jstringSurname= (jstring) env->GetObjectField(jniArrayElement,surnameFieldID);
    jstringEmail= (jstring) env->GetObjectField(jniArrayElement,emailFieldID);    
    jobInfo= env->GetObjectField(jniArrayElement,jobInfoFieldID);
    nativeArray[i].name= jstringToCString(env,jstringName);
    nativeArray[i].surname= jstringToCString(env,jstringSurname);      
        nativeArray[i].email= jstringToCString(env,jstringEmail);
    // .jobInfo
    jobInfo= env->GetObjectField(jniArrayElement,jobInfoFieldID);
       // .companyName
             jstringCompanyName= (jstring) env->GetObjectField(jobInfo,companyNameFieldID);
             nativeArray[i].jobInfo.companyName = jstringToCStringV2(env,jstringCompanyName);
       // .companyName
             jstringCompanyAddress= (jstring) env->GetObjectField(jobInfo,companyAddressFieldID);
             nativeArray[i].jobInfo.companyAddress = jstringToCStringV2(env,jstringCompanyAddress);
             // telNumber
             telNumber= (jstring) env->GetObjectField(jobInfo,telNumberFieldID);  
             nativeArray[i].jobInfo.telNumber = jstringToCStringV2(env,telNumber);
             // faxNumber
             faxNumber= (jstring) env->GetObjectField(jobInfo,faxNumberFieldID);
       nativeArray[i].jobInfo.faxNumber = jstringToCStringV2(env,faxNumber);
   }
   return nativeArray;
}

JNIEXPORT void JNICALL Java_com_example_opencl_utility_OpenCLUtility_execute(JNIEnv *env, jobject obj, jobjectArray arr){
      cl_int err = CL_SUCCESS;
   int arraySize= env->GetArrayLength(arr);
   arraySize=arraySize-arraySize%8;
   printf("INFO: Recieved array of size %d\n",arraySize);  

   PElement inputArray= JniConvertElementArrayToNative(env,&obj,&arr);

   if(Setup_OpenCL("SampleFilter.cl")==-1){
        printf("ERROR: Failed to Setup OpenCL...\n");
        return;      
   }
      // allocate the buffer
      g_inputBuffer = clCreateBuffer(g_context, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, sizeof(TListElement) * arraySize, inputArray, NULL);

      if (g_inputBuffer == (cl_mem)0){
        printf("ERROR: Failed to create Input Buffer...\n");
        return;
      }
      printf("INFO: g_inputBuffer is created (%x)...\n",g_inputBuffer);
      err  =  clSetKernelArg(g_kernel, 0, sizeof(cl_mem), (void *) &g_inputBuffer);
      if (err != CL_SUCCESS) {
        printf("ERROR: Failed to set input g_kernel arguments...\n");
        return;
      }
      printf("INFO: g_kernel parameters are set...\n");

   size_t global_work_size[1] = { arraySize };
      size_t local_work_size[1]= { LOCAL_SIZE };     
   size_t offset[1]= { 0 };     

      QueryPerformanceCounter(&g_PerformanceCountNDRangeStart);

      // execute kernel
      if (CL_SUCCESS != (err = clEnqueueNDRangeKernel(g_cmd_queue, g_kernel, 1, offset, global_work_size, local_work_size, 0, NULL, NULL))){
        printf("ERROR: Failed to run kernel...\n");
        return;
      }
      err = clFinish(g_cmd_queue);
      QueryPerformanceCounter(&g_PerformanceCountNDRangeStop);

   void* tmp_ptr = NULL;
      tmp_ptr = clEnqueueMapBuffer(g_cmd_queue, g_inputBuffer, true, CL_MAP_READ, 0, sizeof(struct ListElement) * arraySize, 0, NULL, NULL, NULL);
   if (tmp_ptr!=inputArray){
      printf("ERROR: clEnqueueMapBuffer failed to return original pointer\n");
       return;
   }

    err = clFinish(g_cmd_queue);
    clEnqueueUnmapMemObject(g_cmd_queue, g_inputBuffer, tmp_ptr, 0, NULL, NULL);
    clReleaseMemObject(g_inputBuffer); 

    //retrieve perf. counter frequency
    QueryPerformanceFrequency(&g_PerfFrequency);
    printf("NDRange perf. counter time %f ms.\n", 
        1000.0f*(float)(g_PerformanceCountNDRangeStop.QuadPart - g_PerformanceCountNDRangeStart.QuadPart)/(float)g_PerfFrequency.QuadPart);
    JniConvertToNativeArrayToElementArray(env,&obj,inputArray,&arr);
   free(inputArray);
   g_inputBuffer = NULL;
}
Bu kodu Windows işletim sisteminde derleyebilmek için aşağıdaki komutu kullanıyoruz:
compile.bat:
cl -EHsc "-I%JAVA_HOME%\include" "-I%JAVA_HOME%\include\win32" "-I%INTELOCLSDKROOT%\include"  -LD %1 OpenCL.lib -Fe%2 /link  /LIBPATH:"%INTELOCLSDKROOT%\lib\x86" OpenCL.lib
build_dll.bat:
call setenv.bat
IF NOT EXIST myopencllib.dll call compile.bat OpenCLUtility.cpp myopencllib.dll
cmd> build_dll.bat
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.21022.08 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

OpenCLUtility.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/dll
/implib:myopencllib.lib
/out:myopencllib.dll
"/LIBPATH:C:\Program Files (x86)\Intel\OpenCL SDK\2.0\lib\x86"
OpenCL.lib
OpenCLUtility.obj
OpenCL.lib

   Creating library myopencllib.lib and object myopencllib.exp

Çekirdek fonksiyonu olarak aşağıdaki SampleFilter.cl dosyası içindeki __kernel void SampleFilter(__global struct Element* pSrc) fonksiyonunu kullancağız:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct JobInfo {
    char* companyName;
    char* companyAddress;
    char* telNumber;
    char* faxNumber;
} ;

typedef struct JobInfo TJobInfo;

struct Element {
    int id;
    int identityCardNo;
    char* name;
    char* surname;
    int age;
    TJobInfo jobInfo;
    char* email;
    bool selected;
    bool masked;
} ;

__kernel void SampleFilter(__global struct Element* pSrc)
{
 const int x = get_global_id(0); 
 const int size = get_local_size(0); 
    pSrc[x].id=pSrc[x].id+5;
    pSrc[x].identityCardNo=pSrc[x].identityCardNo+1000;
    pSrc[x].age=pSrc[x].age+10;
}
Projenin tamamını betiklerle beraber bu adresten indirebilirsiniz.

Monday, July 29, 2013

MySQL Cluster 7.3 GA YAYINLANDI

MySQL Cluster çözümünün uzun süredir beklediğimiz 7.3 GA sürümü nihayet yayınlandı. Yeni sürümü bu adresten indirebilirsiniz. Diğer MySQL sürümlerinde olduğu gibi Cluster ailesini de farklı işletim sistemlerine kurabilirsiniz: Windows (x86 32/64-bit), Solaris (x86 64-bit, Sparc), SuSe, OEL 4/5/6, RHEL 4/5/6, Free BSD, Mac OS X
Yeni sürümle gelen en önemli gelişmeleri aşağıda sıraladım:

  • MySQL Cluster NDB depolama motorunu kullanmaktadır. NDB motoruna MySQL API'si ve NDB API'si üzerinden erişmek mümkündür. Yeni sürümde buna yeni bir bağlantının eklendiğini görüyoruz: NoSQL çözümler için Node.js bağlantısını kullanabilirsiniz.
  • NDB nihayet Foreign Key desteğine kavuştu. InnoDB'de olduğu gibi NDB'de artık veriler topakta nasıl dağılmış olursa olsun, bilgi tutarlılığını (=referential integrity) sağlıyor. 
  • API düğümleri artık MySQL'in en güncel sürümü olan 5.6 sürümünü kullanıyor. Bunun iki kazanımı var:
    1. MySQL 5.6 sürümü çok çekirdekli mimarilerde yüksek ölçeklenebilirliğe sahiptir.
    2. MySQL 5.6 sürümü önceki sürümlere göre daha az hata barındırıyor.
  • MySQL Cluster kurulum için artık bir kurulum sihirbazına sahip.
MySQL Cluster ile ilgili teknik detayları ve başarım karşılaştırması için bu belgeyi okumanızı tavsiye ederim.

GÖDEL KANITLAMASI

Matematikle ilgilenenlerin mutlaka okuması gereken bir diğer kitap: Gödel's Proof. Kitabın çevirisi Boğaziçi Üniversitesi Yayınlarından çıktı.
"Gerçek" matematikçilerin uğraştığı "gerçek" matematiğin neredeyse tamamen yararsız olduğu söylense de saf matematikle uğraşan Gottlob Frege, Georg Cantor ve Richard Dedekind herhangi bir yararlı makine icat etmemişler ama Batıda yeni bir düşünme tarzının temellerini atan bir araç sağlamışlardır. Çağlar boyunca matematiğin kesinlik, tutarlılık, tamlık gibi ideal beklentileri eksiksizce karşılayan bir bilim olduğu düşünüldü. Kesinlik, tutarlılık, tamlık gibi niteliklerin matematiğe yüklenmesinin en önemli nedeni, matematiğin aksiyomlardan türetilen doğru önermelerinin, yani teoremlerin kesin olarak kanıtlanabilir olmasıydı. Matematiğin teoremlerinin doğru iseler, doğrulukları kesinlikle kanıtlanabilen, doğru değilseler de, yine doğru olmadıkları kesin olarak kanıtlanabilen önermeler oldukları, dolayısıyla matematikte kesinlik ve tutarlılığın tam olarak egemen olduğı kabul edilmişti. Gödel'in kanıtlaması bu kabullerin ve beklentilerin doğru olmadığını yine matematikten yola çıkarak kesin olarak kanıtlamıştır. Whitehead ve Russell'ın matematiğin mantıksal temelleri konusundaki dev çalışması olan Principia Mathematica'yı ele alarak temellerin hep eksik kalacağını göstermiştir. Yani doğal sayılar aritmetiğini kapsayan bir biçimsel dizgede öyle önermeler vardır ki, bunların ne doğru ne de yanlış oldukları kanıtlanabilir. Ayrıca Gödel, doğal sayılar aritmetiğini kapsayan bir biçimsel dizgenin tutarlılığının, bu dizgenin kendi içinde kanıtlanamayacağını da kanıtlamıştır. Gödel kanıtlamasının sonuçları matematiğin kendi içsel sınırlılıkları olduğunu ortaya koymuştur. Gödel kanıtlaması mantık ve matematiğin dışına taşan felsefi sonuçlara da sahiptir. Matematiğin ve matematiksel nesnelerin aslî doğası, matematikle mantığın ilişkisi, vb. felsefi meseleleri yeni bir tartışma zeminine taşımıştır. Ayrıca postmodernite üzerine düşünce üreten felsefeciler de Gödel'e sık sık gönderme yapmakta ve bütünselci yaklaşımlara yöneltilen eleştirilerde Gödel'in çalışmalarından da destek bulduklarını düşünmekteler.
 

KİTAP'TAN DELİLLER

Eğer matematik ile ilgileniyorsanız "Proofs from THE BOOK" kitabını mutlaka okumalısınız. Kitabın Türkçe çevirisi İstanbul Bilgi Üniversitesi Yayınlarından çıktı. Kitabı takip edebilmek için üniversitede lisans düzeyinde aldığınız matematik dersleri yeterli olacaktır. 
Kitap'tan Deliller, çağının en üretken ve sıradışı bilim insanlarından olan ve matematikçiyi "kahveyi teoremlere dönüştüren bir makine" olarak tanımlayan, hayatını devamlı seyahat edip matematik va'zederek geçiren Paul Erdös'ün görüşlerini yansıtmaktadır. Ateist olmasına rağmen Tanrı'nın matematik teoremlerinin kanıtlarını tuttuğu bir "Kitab"a inanan Erdös, yazdığı 1500 makaleyle matematik tarihinin en verimli makale yayıncılarından biri olmuştur.  

Wednesday, July 17, 2013

Java'da Soyut Sınıf ve Arayüz Kullanımı

Sınıf, nesne, kalıtım ve çok şekillilik nesneye dayalı programlamanın temel bileşenleridir. Modellemeyi ve gerçeklemeyi bu yapıları kullanarak sağlıyoruz. Java programlama dili de nesneye dayalı bir programlama dilidir. Söz dizimsel özellikleri C++ programlama dilininkine benzer. İşleyişte ise bazı temel farklar bulunur. Bu farklardan biri C++ ve Java'nın çoklu kalıtımı destekleme biçimleriyle ilgilidir. C++, bir sınıfın, birden fazla sınıftan, doğrudan türetilmesine izin verir. Bu çoklu kalıtım olarak adlandırılır. Ancak bu özelliğin sorunlu bazı yönleri bulunmaktadır. Bu sorunlardan birini, çoklu kalıtım uygulamasının yer aldığı aşağıda verilen kod üzerinde görmeye çalışalım. Aşağıdaki kod derlenmek istenirse, derleyici D sınıfının getX() metodu için hata iletisi verir ve derleme başarısız olur.

class A {

  protected:

     int x;

  public:

     virtual int getX(){

        return x;
      }
};
class B : public A {
  public:
      int getX(){
        return 2 * x;
      }
};
class C : public A {
  public:
      int getX(){
        return 3 *x;
      }
};
class D : public B , public C {
  public:
      int getX(){
        return 4 * x;
      }
};
Yukarıdaki çoklu kalıtım uygulamasında, türetme yapılan sınıfların ortak bir sınıftan türetilmiş olmaları durumunda, bu ortak sınıflardan gelen veriler, kalıtım yoluyla birden fazla kere tekrarlı olarak türetilmiş sınıfa aktarılır. Bu nedenle türetilmiş sınıfta, tekrarlı elemana erişimin olduğu yerlerde, derleyici belirsizlik hatası verecektir. Derleyici B üzerinden gelen x'e mi yoksa C sınıfı üzerinden gelen x'e mi erişilmek istenildiğine karar veremez. Yukarıdaki tasarımda A sınıfında tanımlanan x hem B hem de C üzerinde tekrarlı olarak D sınıfına aktarılır. C++'da bu problemin sezgisel olmayan sanal kalıtım olarak adlandırılan bir çözümü bulunmaktadır. Burada B ve C sınıfını kodlayan programcılar A sınıfından türetme yaparken sanal kalıtım uygulaması yaparlar:
class B : virtual public A {
  public:
      int getX(){
         return 2 * x;
      }
};
class C : virtual public A {
  public:
      int getX(){
        return 3 *x;
      }
};
Şimdi D sınıfına, A sınıfından kalıtımla sadece tek bir x aktarılır. Bu çözümün doğal olmayan bir yönü bulunmaktadır. Eğer B ve C sınıfları farklı programcılar tarafından kodlanıyorsa bu çözümün öngörülemez olduğu açıktır. Üstelik programcılardan birinin sanal kalıtımı uygulaması ancak diğer programcının uygulamaması durumunda önerilen çözüm çalışmaz. Java'da bir sınıfın sadece tek bir sınıfı türetebiliyor olmasının temel nedeni bu türden durumlar yaratmamaktır. Böylelikle Java'da yukarıda C++ için verilen örnekteki gibi bir durum tekli kalıtım kullanılması nedeni ile asla oluşmaz.
Fiziksel uzayda (problem uzayında) bazen doğal olarak çoklu kalıtım durumları ile karşılaşıyoruz. Bu gibi durumlarda Java'nın sağladığı çözüm arayüz (=interface) kullanmaktır. Az sonra Java ara yüzlerinin özelliklerini detaylı olarak çalışacağız. Ancak kısaca söylemek gerekirse, Java'da bir sınıf sadece tek bir sınıfından türetilebilirken (extends), birden fazla arayüzü gerçekleyebilir (=implements). Üstelik Java arayüzleri arasında çoklu kalıtım vardır: bir arayüz birden fazla arayüzden türetilebilir. Arayüz kullanıldığında, çoklu kalıtımın C++'daki gibi bir yan etkisi ile karşılaşılmamasının nedeni, arayüzlerde öznitelik tanımlanmasına izin verilmemesidir. Soyut sınıflar ile arayüz arasında önemli bir ilişki vardır. Az sonra bu ilişkinin özeliklerini çalışacağız. Ancak önce soyut sınıfları çalışalım.
Çok şekillilik için aşağıdaki üç özelliğin sağlanması gerekir:
  1. Aralarında kalıtım ilişkisi olan sınıflar,
  2. Bu sınıflarda yer alan çok şekilli metotlar,
  3. Bu sınıflar arasında en temel sınıfından tanımlanmış bir referans.
Bu referansı kullanarak yapılan tüm çağrılar, yürütme zamanında bağlanır ve dinamik bağlama olarak adlandırılır. Çok şekillilik için gerekli olan birinci koşul çoğu zaman doğal olarak oluşur ancak bazen nadir de olsa oluşmaz. Örneğin Bird, Airplane, Ufo (Unidentified Funny Objects) ve Superman sınıfları arasında kalıtım ilişkisi yoktur. Bird, Airplane değildir, Superman, Ufo değildir. Ancak bu dört sınıfta da ortak bir davranış vardır: fly(). fly() metodu çok şekilli bir metottur. Ancak çok şekilli bir metodun varlığı, sınıflar arasında kalıtım ilişkisi kurulamadığı için çok şekillilik için yeterli olmaz. Sadece çok şekilliliği kurabilmek için gerçek dünyada olmayan bir sınıf uydurulur. Bu sınıf Flyable olsun. Şimdi problem uzayımızdaki tüm sınıflar (Bird, Airplane, Ufo ve Superman) bu uydurulan sınıftan türetilir. Bu sınıfın gerçek dünyada olmayan, sırf çok şekillilik için kafamızdan "uydurduğumuz" bir sınıf olduğunu betimlemek üzere sınıfı soyut sınıf olarak adlandırıyoruz. Java'da soyut sınıf tanımlaması abstract anahtar kelimesi kullanılarak yapılır. Soyut bir sınıfın içinde en az bir tane soyut bir metot olması beklenir. Soyut metot abstract anahtar kelimesi ile tanımlanır. Örneğimizdeki Flyable soyut sınıfı aşağıdaki gibi tanımlanır:
public abstract class Flyable {
   public abstract void fly();
}
Sanal sınıflar gerçek dünyada var olmadıkları için çözüm uzayında da var olmamaları gerekir. Bu nedenle soyut sınıftan nesne yaratılmasına izin verilmez. Soyut sınıftan türetilen sınıflarda, soyut metotlara mutlaka işlev yüklemek gerekir:
public class Bird extends Flyable {
     @Override
     public void fly() {
       System.out.println("Bird is flying now...");
     }
}
public class Airplane extends Flyable {
     @Override
     public void fly() {
       System.out.println("Airplane is flying now...");
     }
}
public class Ufo extends Flyable {
     @Override
     public void fly() {
       System.out.println("Ufo is flying now...");
     }
}
public class Superman extends Flyable {
     @Override
     public void fly() {
       System.out.println("Superman is flying now...");
     }
}
Soyut sınıftan nesne yaratamıyor olsak da soyut bir sınıfın verisi ve kurucu metodu bulunabilir. Soyut bir sınıf başka bir soyut sınıftan türetilebilir. Bu durumda soyut sınıfın soyut metotlarına işlev yüklemesi gerekmez. Soyut bir sınıf bir arayüzü gerçekleyecek ise onun metotlarına işlev yüklemek zorunda değildir. Bu durumda arayüzün metotları soyut sınıfın soyut metotlarına dönüşür. Soyut sınıfın somut metotları olabilir. Somut metot gövdesini kıvırcık parantez kullanarak verdiğimiz, gerçek dünyada örneği ile karşılaşabildiğimiz ve bunun bir sonucu olarak da modelleyebildiğimiz metotlardır.
Java arayüzleri özel bir tür soyut sınıftır. Arayüzlerin verisi, kurucusu, somut metodu olamaz:
           Soyut Sınıf = Arayüz + Kurucu + Veri + Somut Metod
    Arayüz = Soyut Sınıf  - Kurucu - Veri - Somut Metod
Tüm metotları soyut olmak zorundadır. Üstelik bu soyut metotlar public tanımlı olmak zorundadır. Arayüz metotları hem public hem de abstract tanımlı olmak zorunda oldukları için tanımlanırken bunun açık olarak belirtilmesi gerekmez. Örtük olarak public ve abstract olduğu kabul edilir. Yukarıdaki Flyable soyut sınıfının verisi ve somut metodu olmadığı için arayüz olarak da gerçeklenebilir. Arayüz tanımını interface anahtar kelimesini kullanarak veriyoruz:
public abstract interface Flyable {
   public abstract void fly();
}
Bu durumda, Bird, Airplane, Ufo ve Superman sınıflarını Flyable arayüzünü kullanarak implements anahtar kelimesi ile tanımlıyoruz:
public class Bird implements Flyable {
     @Override
     public void fly(){
       System.out.println("Bird is flying now...");
     }
}
public class Airplane implements Flyable {
     @Override
     public void fly(){
       System.out.println("Airplane is flying now...");
     }
}
public class Ufo implements Flyable {
     @Override
     public void fly(){
       System.out.println("Ufo is flying now...");
     }
}
public class Superman implements Flyable {
     @Override
     public void fly(){
       System.out.println("Superman is flying now...");
     }
}
Daha gelişmiş bir örneği NetBeans projesi olarak bu bağlantıdan indirebilirsiniz. Örnek modellemeyi daha rahat takip edebilmeniz için oluşturduğum UML sınıf diyagramını aşağıda bulabilirsiniz: