Saturday, October 19, 2013

Spring Çatısına Kendi Injector Sınıfımızı Eklemek

Nesneye dayalı yaklaşım ile tasarım yapıldığında uyulması tavsiye edilen bir dizi ilke vardır. Bu ilkelerden biri yüksek uyum ve zayıf bağdır (=High Cohesion-Low Coupling). Uyumluluk bir sınıfın metotları için tanımlanır. Eğer sınıfın metotları aynı problemin çözümüne odaklanmışlar ise sınıfın metotları arasında bir uyumdan bahsedilir. Yazdığımız her sınıfa tek bir sorumluluk verirsek ya da tek bir konuda uzmanlaşmasını sağlarsak sınıf metotları arasında yüksek uyum sağlamış oluruz. Böylelikle sınıfı değiştirmek için tek bir nedenimiz olur. Ancak yüksek uyumluluk sağlamaya çalışırken, ortaya başka bir problem çıkar. Bir sınıf metodu işini yaparken kendi uzmanlığı dışında bir iş yapması gerektiğinde, bu işi uzmanına ihale edecektir. Bu durumda sınıflar arasında hizmet alma ve verme ilişkisi ortaya çıkar. Bir sınıf hem istemci hem de sunucu rolüne sahip olabilir. İstemci sınıf hizmet alacağı sınıfın referansını içerir. Böylelikle sınıflar arasında bağımlılıklar ortaya çıkacaktır. Bu bağımlılığı yönetebilmek gerekir. Bu bağımlılıktan dolayı oluşan bağın zayıf olmasını sağlamak üzere istemci sınıf hizmet alacağı sınıfın referansını doğrudan içermez. Bunun yerine istemci ile sunucu rollerindeki sınıflar arasında bir arayüz/sözleşme tanımlanır. Sınıflar bir birlerini doğrudan görmezler. İstemci sınıf bu sözleşmeye göre hizmet alır, servis sınıfı da bu sözleşmeye bağlı kalarak hizmet verir. İstemci sınıf ileride kodda değişiklik yapmadan hizmet alacağı sınıfı değiştirebilir. Nasıl cep telefon numaranızı değiştirmeden GSM operatörünüzü değiştirebiliyorsanız ya da internet servis sağlayıcınızı değiştirdiğinizde ADSL modeminizi değiştirmeniz gerekmiyorsa, istemci sınıf da hizmet aldığı sınıfı değiştirdiğinde kodda değişiklik yapması gerekmemelidir. Sözleşmeyi soyut sınıf ya da genellikle arayüz ile tanımlıyoruz. Bu şekilde istemci ve sunucu sınıflar arasındaki bağı gevşek tutmuş oluyoruz. Bu bağı daha da gevşetmenin yolu Java ya da C# programlama dillerindeki damgalama/not düşme yeteneğini kullanmaktır. Bu yaklaşımı genellikle bileşen tabanlı yazılım geliştirmede kullanıyoruz. Bileşenler aynı sınıflardan yarattığımız nesneler gibidir. Ancak onlardan farklı olarak yaşayabilmeleri, var olabilmeleri için bir barınağa ya da kaba (=container) ihtiyaç duyarlar. Barınak bileşenlerin yaşam döngülerini yönetir ve bazı sık kullanılan hizmetler sunar. Kurumsal Java uygulamalarındaki uygulama sunucuları Web kabı ve EJB kabı olmak üzere iki kap ve bileşen modeli içerir. Web kabında Servlet ve JSP bileşen modelleri yer alır. EJB kabında ise Session Bean, Message Driven Bean bileşen modelleri bulunur. Bu bileşenler, uygulama sunucusunun sağladığı hizmetlerden standart bir dizi damga yardımıyla yararlanır.
Arayüz kullanarak bağı zayıflatmış olsak da yürütme zamanında istemci sınıfa hangi sınıftan hizmet alacağını söylememiz gerekir. Yani bağımlılıkların çözülmesi gerekir. Karmaşık sınıf ilişkilerinin olduğu bir yazılımda bağımlılıkları görmek ve düzgün sırada bağımlıkları çözmek zordur. Bunun için genellikle Fabrika Kalıbı kullanılır. Karmaşık nesneleri yaratma sorumluluğu bu fabrika sınıflarına verilir. Ancak bu durumda çok sayıda fabrika tasarlamak gerekir. Üstelik nesnelerin  bağlantıları değiştirildiğinde fabrika sınıfını değiştirmek ya da yeni bir fabrika sınıfı yazmak gerekecektir. Spring çatısı bu bağları otomatik olarak çözebilen genelleştirilmiş bir fabrika sunar. Bu fabrika her türlü sınıftan nesneyi tüm bağımlılıkları ile birlikte yaratabilme yeteneğine sahiptir. Bunun dışında istersek biz de kendi Injector sınıfımızı yazmamıza imkan verir. Bu yazının konusu bu tür bir Injector sınıfının nasıl yazılacağı ve Spring IoC kabına tanıtılması ile ilgilidir.
Bunun için org.springframework.beans.factory.config.BeanPostProcessor arayüzünü gerçekleyen bir sınıf, com.example.mod04.processors.RandomPostProcessor, tasarlıyoruz:

1:  package com.example.mod04.processors;  
2:    
3:  import com.example.mod04.annotation.RandomNumberGenerator;  
4:  import org.springframework.beans.BeansException;  
5:  import org.springframework.beans.factory.config.BeanPostProcessor;  
6:    
7:  public class RandomPostProcessor implements BeanPostProcessor {  
8:    
9:       @Override  
10:       public Object postProcessAfterInitialization(Object bean, String beanname) throws BeansException {  
11:            RandomNumberGenerator.inject(bean);  
12:            return bean;  
13:       }  
14:    
15:       @Override  
16:       public Object postProcessBeforeInitialization(Object bean, String beanname) throws BeansException {  
17:            return bean;  
18:       }  
19:    
20:  }  

Buradaki RandomRandomGenerator sınıfının kodunu aşağıda bulabilirsiniz:
1:  package com.example.mod04.annotation;  
2:    
3:  import java.lang.reflect.*;  
4:  import java.util.Collections;  
5:  import java.util.List;  
6:    
7:  public class RandomNumberGenerator {  
8:       public static int random(int min, int max) {  
9:            int len = max - min + 1;  
10:            return (int) (Math.random() * len + min);  
11:       }  
12:    
13:       public static void inject(Object obj) {  
14:            Class<? extends Object> cls = obj.getClass();  
15:            for (Field field : cls.getDeclaredFields()) {  
16:                 if (field.isAnnotationPresent(Random.class)) {  
17:                      Class<? extends Object> type = field.getType();  
18:                      Random random = field.getAnnotation(Random.class);  
19:                      int minValue = random.minValue();  
20:                      int maxValue = random.maxValue();  
21:                      int size = random.size();  
22:                      boolean sorted = random.sorted();  
23:                      boolean unique = random.unique();  
24:                      field.setAccessible(true);  
25:                      if (type.isPrimitive()) {  
26:                           try {  
27:                                field.set(obj, random(minValue, maxValue));  
28:                           } catch (IllegalArgumentException | IllegalAccessException e) {  
29:                                e.printStackTrace();  
30:                                return;  
31:                           }  
32:                      }   
33:                      else if (type.equals(List.class)) {  
34:                           List<Integer> list;  
35:                           try {  
36:                                list = (List<Integer>) field.get(obj);  
37:                           } catch (IllegalArgumentException | IllegalAccessException e) {  
38:                                e.printStackTrace();  
39:                                return;  
40:                           }  
41:                           for (int i = 0; i < size; ++i) {  
42:                                int candidate;  
43:                                do {  
44:                                     candidate = random(minValue, maxValue);  
45:                                     if (!unique)  
46:                                          break;  
47:                                } while (list.contains(candidate));  
48:                                list.add(candidate);  
49:                           }  
50:                           if (sorted)  
51:                                Collections.sort(list);  
52:                      }  
53:                 }  
54:            }  
55:       }  
56:  }  

Random damgasının tanımı aşağıda verilmiştir. Random damgası sadece sınıf özniteliklerine uygulanabilir, yürütme zamanında tip bilgisine, Class sınıfı üzerinden erişilebilinir. Random damgasının minValue, maxValue, unique, sorted, size öznitelikleri ile rastgele sayı üretilmesi sağlanmaktadır. Bu damgayı RandomNumberGenerator hizmet sınıfı kullanmaktadır. Bu sınıfın tek bir metodu bulumaktadır: inject. Bu metot parametre olarak Object sınıfından bir referans alır. java.lang.reflect paketini kullanarak nesnenin tip bilgisine erişerek, Random damgası ile damgalanmış bir özniteliği olup olmadığını sorgular. Bu damga ile damgalanmış bir özniteliği varsa damganın minValuemaxValueuniquesortedsize değerlerine uygun olarak bir rastgele değer belirler ve  nesnenin özniteliğinin değerini bu değer ile değiştirir. Random damgasının minValuemaxValueuniquesortedsize değerlerinin hepsi için bir varsayılan değer tanımlanmıştır. Dolayısı ile sadece @Random() ile damgalama yapılabilinir.
1:  package com.example.mod04.annotation;  
2:    
3:  import java.lang.annotation.Documented;  
4:  import java.lang.annotation.ElementType;  
5:  import java.lang.annotation.Retention;  
6:  import java.lang.annotation.RetentionPolicy;  
7:  import java.lang.annotation.Target;  
8:  @Documented  
9:  @Target(ElementType.FIELD)  
10:  @Retention(RetentionPolicy.RUNTIME)  
11:  public @interface Random {  
12:       int minValue() default 1;  
13:       int maxValue() default 100;  
14:       boolean unique() default false;  
15:       boolean sorted() default false;  
16:       int size() default 1;   
17:  }  
Şimdi bir kaç Random damgası ile damgalanmış sınıf örneğine bakalım:
1:  package com.example.mod04.domain;  
2:  import com.example.mod04.annotation.Random;  
3:    
4:    
5:  public class Circle {  
6:       @Random() private int x;  
7:       @Random() private int y;  
8:       @Random(minValue=20,maxValue=40) private int radius;  
9:         
10:       public Circle() {            
11:       }  
12:         
13:       public Circle(int x, int y, int radius) {  
14:            this.x = x;  
15:            this.y = y;  
16:            this.radius = radius;  
17:       }  
18:    
19:       public int getX() {  
20:            return x;  
21:       }  
22:       public void setX(int x) {  
23:            this.x = x;  
24:       }  
25:       public int getY() {  
26:            return y;  
27:       }  
28:       public void setY(int y) {  
29:            this.y = y;  
30:       }  
31:       public int getRadius() {  
32:            return radius;  
33:       }  
34:       public void setRadius(int radius) {  
35:            this.radius = radius;  
36:       }  
37:    
38:       @Override  
39:       public String toString() {  
40:            return "Circle [x=" + x + ", y=" + y + ", radius=" + radius + "]";  
41:       }  
42:  }      

1:  package com.example.mod04.domain;  
2:    
3:  import java.util.ArrayList;  
4:  import java.util.List;  
5:    
6:  import com.example.mod04.annotation.Random;  
7:    
8:  public class Lottery extends Object {  
9:       @Random(minValue=1,maxValue=49,size=6,unique=true,sorted=true)   
10:       private List<Integer> numbers;  
11:    
12:       public Lottery() {  
13:            numbers= new ArrayList<Integer>();  
14:       }  
15:    
16:       @Override  
17:       public String toString() {  
18:            return "Lottery "+numbers;  
19:       }  
20:    
21:       public List<Integer> getNumbers() {  
22:            return numbers;  
23:       }  
24:  }  
Spring IoC kabının yapılandırma dosyası, spring-config.xml, ise aşağıdaki gibi oluşturulmalıdır:
1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <beans xmlns="http://www.springframework.org/schema/beans"  
3:       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
4:       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
5:     <bean class="com.example.mod04.processors.RandomPostProcessor"/>  
6:     <bean id="cember" class="com.example.mod04.domain.Circle"/>  
7:     <bean id="sayisal" class="com.example.mod04.domain.Lottery"/>   
8:  </beans>  
Örnek uygulama ve çıktısı aşağıda verilmiştir:
1:  package com.example.mod04.console;  
2:    
3:  import com.example.mod04.domain.Circle;  
4:  import com.example.mod04.domain.Lottery;  
5:    
6:  import org.springframework.context.ConfigurableApplicationContext;  
7:  import org.springframework.context.support.ClassPathXmlApplicationContext;  
8:    
9:  public class TestRandomPostProcessor {  
10:    
11:       /**  
12:        * @param args  
13:        */  
14:       public static void main(String[] args) {  
15:            ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");  
16:            Circle circle= context.getBean("cember",Circle.class);  
17:            System.out.println(circle);  
18:            Lottery sayisal= context.getBean("sayisal",Lottery.class);  
19:            System.out.println(sayisal);  
20:            context.close();  
21:       }  
22:  }  

 Oct 19, 2013 12:02:21 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh  
 INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@17ac9cff: startup date [Sat Oct 19 12:02:21 EEST 2013]; root of context hierarchy  
 Oct 19, 2013 12:02:21 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions  
 INFO: Loading XML bean definitions from class path resource [beans.xml]  
 Oct 19, 2013 12:02:21 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons  
 INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7393ccd5: defining beans [com.example.mod04.processors.RandomPostProcessor#0,cember,sayisal]; root of factory hierarchy  
 postProcessAfterInitialization()  
 postProcessAfterInitialization()  
 Circle [x=50, y=17, radius=33]  
 Lottery [7, 14, 23, 34, 35, 43]  
 Oct 19, 2013 12:02:21 PM org.springframework.context.support.AbstractApplicationContext doClose  
 INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@17ac9cff: startup date [Sat Oct 19 12:02:21 EEST 2013]; root of context hierarchy  
 Oct 19, 2013 12:02:21 PM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons  
 INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@7393ccd5: defining beans [com.example.mod04.processors.RandomPostProcessor#0,cember,sayisal]; root of factory hierarchy  
Projenin tamamını Maven projesi olarak bu adresten indirebilirsiniz. Spring konusunda daha fazla bilgi edinmek isterseniz bu bağlantıdaki Spring sınıf eğitimlerini tavsiye ederim.

Monday, September 9, 2013

JBoss AS 7.1+'de MySQL Veritabanı kullanarak Kimlik Doğrulama

Java EE platformu kullanılarak kurumsal uygulama geliştirirken tasarımda düşünülmesi gereken bileşenlerden biri de güvenliktir. Bu amaçla Java EE platformunun rol tabanlı güvenlik modelinden yararlanılır. Buna göre kaynaklar ile bu kaynaklara erişim yetkisi olan roller ilişkilendirilir. Roller ile kullanıcılar arasındaki bağlantı ise Java EE bağlamı dışında düşünülür. Kullanıcı bilgileri genellikle veritabanı ya da LDAP gibi bir dizin sunucusunda saklanılır ve kimlik doğrulama işlemi bu sunuculara ihale edilir. JBoss AS 7 Java EE 6 uyumlu, açık kaynak kodlu, Red Hat firmasından desteğini alabileceğiniz, modüler bir mimariye sahip, çevik bir uygulama sunucusudur.  JBoss AS 7.2 sürümü ile kısa bir süre önce Red Hat yazılım ürününe dönüşmüştür ve EAP (=Enterprise Application Platform) olarak yayınlanmaktadır. Güncel sürümünü (JBoss EAP 6.1) bu adresten indirebilirsiniz. Kurulum için tek yapmanız gereken sıkıştırılmış arşiv dosyasını uygun bir dizine açmaktır. Bağımsız ve alan olmak üzere iki farklı kipte kullanabilirsiniz. Bağımsız kipte çalıştırmak için standalone.bat (standalone.sh) betiğini ve alan kipinde çalıştırmak için domain.bat (domain.sh) betiğini kullanıyoruz. Betikleri çalıştırmadan önce iki sistem değişkenine atama yapmanız gerekir:
c:\opt\jboss-eap-6.1>set JAVA_HOME=c:\opt64\java\jdk1.7.0_25
c:\opt\jboss-eap-6.1>set JBOSS_HOME=c:\opt\jboss-eap-6.1

c:\opt\jboss-eap-6.1>set PATH=%JAVA_HOME%\bin;%JBOSS_HOME%\bin;%PATH%
Bundan sonra betiklerden birini kullanarak sunucuyu bağımsız ya da alan kipinde çalıştırabilirsiniz:

Bağımsız kipte çalıştırıldığında varsayılan yapılandırma dosyası standalone.xml ve alan kipinde çalıştırıldığında varsayılan yapılandırma dosyası ise domain.xml ve host.xml dosyalarıdır. Kimlik doğrulama ile ilgili tanımlamalar hangi kipte sunucuyu kullandığınıza göre standalone.xml ya da domain.xml dosyasında aynı şekilde yapılmaktadır.

Kimlik Doğrulama için İlişkisel Veritabanı Sunucusu Kullanımı

Bu örnek yapılandırmada MySQL 5.6 veritabanını kullanacağız. Öncelikli olarak bir veritabanı ve bu veritabanında iki adet tablo yaratacağız:

mysql> create database userdb;
Query OK, 1 row affected (0.00 sec)

mysql> use userdb;
Database changed
mysql> CREATE TABLE USERS(
    ->    login VARCHAR(64) PRIMARY KEY,
    ->    passwd VARCHAR(64)
    -> ) engine=InnoDB;
Query OK, 0 rows affected (0.32 sec)

mysql> CREATE TABLE USER_ROLES(
    ->    login VARCHAR(64),
    ->    role VARCHAR(32)
    -> ) engine=InnoDB;
Query OK, 0 rows affected (0.38 sec)
Veritabanında USERS tablosunda kullanıcı adı ve parolası ve USER_ROLES tablosunda ise rol ve kullanıcı ilişkileri saklanmaktadır. Örnek bir kullanıcı ve rol ekleyelim:
mysql> INSERT into USERS values('jackb','Xr4ilOzQ4PCOq3aQ0qbuaQ==');
Query OK, 1 row affected (0.07 sec)

mysql> INSERT into USER_ROLES values('jackb', 'WEB_USER');

Query OK, 1 row affected (0.04 sec)
Veritabanında parolanın kendisini değil MD5 ile elde edilmiş özetini ve Base64 ile kodlanmış değerini saklıyoruz. Bu kodlanmış değeri elde etmek için aşağıdaki komutu kullanabilirsiniz:
$ echo -n secret | openssl dgst -md5 -binary | openssl base64
Xr4ilOzQ4PCOq3aQ0qbuaQ==
Şimdi artık standalone.xml yapılandırma dosyasında kimlik doğrulama için gerekli tanımlamaları yapabiliriz:
<?xml version='1.0' encoding='UTF-8'?>
<server xmlns="urn:jboss:domain:1.4">
      .
      .
      .
       <subsystem xmlns="urn:jboss:domain:datasources:1.1">
            <datasources>
               .
               .
               .
               <datasource jta="true" jndi-name="java:/usersDS" pool-name="userdb_pool" enabled="true" 
                        use-java-context="true" use-ccm="true">
                    <connection-url>jdbc:mysql://localhost:3306/userdb</connection-url>
                    <driver>mysql</driver>
                    <pool>
                        <min-pool-size>1</min-pool-size>
                        <max-pool-size>5</max-pool-size>
                        <prefill>true</prefill>
                        <use-strict-min>true</use-strict-min>
                    </pool>
                    <security>
                        <user-name>root</user-name>
                        <password>root</password>
                    </security>
                    <timeout>
                        <idle-timeout-minutes>0</idle-timeout-minutes>
                        <query-timeout>600</query-timeout>
                    </timeout>
                    <statement>
                        <track-statements>true</track-statements>
                        <prepared-statement-cache-size>2</prepared-statement-cache-size>
                        <share-prepared-statements>true</share-prepared-statements>
                    </statement>
                </datasource>
            </datasources>
        </subsystem>
        .
        .
        .
        <subsystem xmlns="urn:jboss:domain:security:1.2">
            <security-domains>
                <security-domain name="database" cache-type="default">
                    <authentication>
                        <login-module code="Database" flag="required">
                            <module-option name="dsJndiName" value="java:/usersDS"/>
                            <module-option name="principalsQuery" value="select passwd from USERS where login=?"/>
                            <module-option name="rolesQuery" value="select role, 'Roles' from USER_ROLES where login=?"/>
                            <module-option name="hashAlgorithm" value="MD5"/>
                            <module-option name="hashEncoding" value="BASE64"/>
                        </login-module>
                    </authentication>
                </security-domain>
                .
                .
                .
            </security-domains>

        </subsystem>

        .
        .
        .
</server>
Bir kurumsal uygulamanın bu bu yapılandırmadan yararlanabilmesi için standart dağıtım yapılandırma dosyası web.xml'de aşağıdaki gibi bir kısıtlama tanımı yapılmalıdır.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>HtmlAuth</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>WEB_USER_ROLE</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Database</realm-name>
</login-config>
<security-role>
<role-name>WEB_USER_ROLE</role-name>
</security-role>
</web-app>
Son olarak uygulama rolü ile veritabanında tanımlanan rol arasında mutlaka aşağıdaki gibi bir eşleme yapılması gerekmektedir. Bu eşleştirmeyi JBoss uygulama sunucusuna özel yapılandırma dosyasında, jboss-web.xml,  yapıyoruz:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web> 
  <context-root>/game</context-root>
  <security-domain>java:/jaas/database</security-domain>
  <security-role>
      <role-name>WEB_USER</role-name>
      <principal-name>WEB_USER_ROLE</principal-name>
  </security-role>
</jboss-web>

Wednesday, August 7, 2013

Linux'da pthread Kütüphanesi ile Çok Çekirdekli Programlama

İşlemcilerin çekirdek sayıları her yeni modelde artmaya devam ediyor. Dolayısı ile eskiden olduğu gibi problemler seri çözülmeye çalışıldığında başarım sınırlı olur, çözüm dikey olarak ölçeklenemez. Çözümü iki çekirdekli bir makinadan 8 çekirdekli bir makinaya taşıdığınızda başarım değişmez. Hangi platform üzerinde ya da hangi programlama dilini kullanarak uygulama geliştiriyor olursanız olun, çözümünüze çok çekirdekli programlamayı tanıştırmanız gerekir. 
Linux platformunda temel geliştirme dili C/C++'dır. Çok parçacıklı programlama için ise Native POSIX Thread (pthread) kütüphanesini kullanıyoruz. pthread bir parçacığı belirli bir çekirdeğe atamak olanağı veriyor. 10 milyon elemanlı bir dizinin elemanları toplamını paralel olarak hesaplayan kodu pthread kütüphanesi kullanarak C++'da kodladım. Aşağıda çözümün kaynak kodunu inceleyebilirsiniz. Kodu derlemek için aşağıdaki komutu çalıştırmanız gerekir:

[student@godel1 mod01]$ g++ parallel_sum.cpp -o parallel_sum -lpthread -pthread
[student@godel1 mod01]$ ./parallel_sum 
Number of virtual cpu's is 2.
Sum of array (Serial) is 45011704
Sum of array (Parallel) is 45011704

Çözümde N adet iş parçacığı kullanılıyor. Bu N adet iş parçacığı int bind_self_to_core(int core_id) fonksiyonu yardımı ile çekirdeklere eşit olarak dağıtılıyor. İş parçacığının yaptığı iş ise void * sum (void *param) fonksiyonunda kodlanmıştır.

parallel_sum.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
#include <iostream>
#include <pthread.h>
#include <cstdlib>
#include <unistd.h>

using namespace std;

const int NUMBER_OF_CORES =  sysconf(_SC_NPROCESSORS_ONLN);

struct problem {
    int *array;
    int start;
    int size;
} ;

typedef struct problem sproblem;

int bind_self_to_core(int core_id) {
    core_id = core_id % NUMBER_OF_CORES ;

    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    CPU_SET(core_id, &cpuset);

    pthread_t current_thread = pthread_self();
    return pthread_setaffinity_np(current_thread, sizeof(cpu_set_t), &cpuset);
}

void * sum (void *param) {
  static int counter=0;
  bind_self_to_core(counter++);
  struct problem *p= (struct problem*) param ;
  int *arr= p->array + p->start;
  long partialSum= 0;
  for (int i=0;i<p->size;++i,++arr){
      partialSum += arr[0];
  }
  return (void*) partialSum;
}

const int N = 10;
const int ARRAY_SIZE = 10000000;
const int PROBLEM_SIZE= ARRAY_SIZE/N;

int main () {
    pthread_t *threads= new pthread_t[N];
    struct problem *problem_instances= new struct problem[N];
    int *array= new int[ARRAY_SIZE];
    int *p= array;
    long sumOfArray=0;
    cout << "Number of virtual cpu is " << NUMBER_OF_CORES << "." << endl ;
    for (int i=0;i<ARRAY_SIZE;++i,++p){
        p[0]= rand() % 10 ;
        sumOfArray += p[0];
    }
    cout << "Sum of array (Serial) is " << sumOfArray << endl;
    /* Create n threads, each working with a different array partition. */
    for (int i=0,start=0;i<N;++i,start+=PROBLEM_SIZE){
        problem_instances[i].array= array;
        problem_instances[i].size= PROBLEM_SIZE;
        problem_instances[i].start= start;
        pthread_create (threads+i, NULL, sum, (void *) (problem_instances+i));
    }
    sumOfArray=0;
    for (int i=0;i<N;++i){
        long partialSum;
        pthread_join (threads[i], (void**)&partialSum);
        sumOfArray += partialSum;
    }
    cout << "Sum of array (Parallel) is " << sumOfArray << endl;
    delete[] array;
    delete[] threads;
    delete[] problem_instances;
        return 0;
}

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.