GPGPU (eng. General-purpose computing on graphics processing units) predstavlja primjenu grafičkih kartica (koje se najčešće koriste samo u računalnoj grafici) za provođenje izračuna u aplikacijama koje najčešće koriste samo CPU procesor.
Grafičke kartice se mogu koristiti za takve izračune jer podržavaju dovoljan broj instrukcija i mogućnosti, pa se mogu koristiti za sve vrste izračuna. Osim toga, korištenje većeg broja grafičkih kartica na jednom računalu ili velikog broja grafičkih kartica raspodjeljenih na više računala omogućuje daljnju paralelizaciju već paralelnog načina provođenja izračuna u grafičkim karticama.
Trenutno najčešće korišteni otvoreni jezik za GPGPU je OpenCL. A kod jezika koji nisu otvorene prirode najčešće se koristi Nvidia CUDA.
GPGPU i ostale slične tehnologije koje se koriste radi boljih performansi kod izračuna počele su se koristiti zbog povećane potrebe za performansama u znanosti i inženjerstvu. Te tehnologije se najčešće koriste za paralelno provođenje različitih izračuna nad velikim skupovima podataka. Potreba za tim tehnologijama stvorila je jaku potrebu za arhitekturama za razvoj softvera koje omogućuju razvoj softvera koji je sposoban da iskoristi grafičke kartice i slične tehnologije za masivno paralelno izvođenje različitih algoritama. Obrada opće namjene na grafičkim procesnim jedinicama (GPGPU) je prilično novi trend u istraživanjima računalnih inženjera. GPU-i su visoko optimizirani koprocesori za obradu računalne grafike. Obrada računalne grafike je područje u kojem dominiraju paralelne podatkovne operacije – točnije, matrične operacije linearne algebre.

Stream processing

Predstavlja izvođenje instrukcija nad velikim brojem nezavisnih grupa podataka istovremeno. Stream je skup podataka koji zahtjevaju jednaku obradu. Oni omogućuju paralelizaciju.
Kod izračuna na grafičkoj kartici svaki od elemenata može se čitati, na temelju njegove vrijednosti izračunati izlaz te izlaznu vrijednost spremiti na izlaznu memorijsku lokaciju. Moguće je da postoje više ulaznih i izlaznih memorijskih lokacija, ali nije moguće da postoji memorijska lokacija iz koje se čitaju i u koju se upisuju podaci.
Za primjenu GPGPU-a je bitan pojam aritmetičke intenzivnosti. Ona je definirana kao broj operacija izvedenih nad jednom jedinicom prenesene memorije (npr. jedan long ili double). Bitno je da GPGPU aplikacije imaju visok aritmetički intenzitet, jer bi inače spora memorijska latencija smanjila ubrzanje izračuna.
GPGPU je najbolje primjeniti za izračune nad velikim skupovima podataka koji podržavaju visoki stupanj paralelizacije i imaju minimalnu međuzavisnost.

Kerneli

Kerneli su funkcije koje se izvode nad svakim elementom stream-a. Kerneli se pišu u programskom jeziku baziranom na ANSI C99 jeziku.
OpenCL logo
OpenCL je otvoreni standard kojeg održava neprofitni konzorcij Khronos Group.
OpenCL je novi industrijski standard za podatkovno i zadačno paralelno provođenje izračunavanja na mnogim različitim vrstama procesora (CPU, GPU, DSP i sl.).
OpenCL definira skup osnovnih funkcionalnosti koje podržavaju svi uređaji. Osim toga, OpenCL definira i neke dodatne funkcionalnosti koje se mogu koristiti samo na određenim uređajima. Iako OpenCL garantira portabilnost koda i mogućnost izvršavanja na različitim uređajima (CPU, GPU i sl.), ta portabilnost ima svojih granica a ona ovisi o veličini razlike hardverske rahitekture uređaja. Ipak, programer ima mogućnost da piše program za jednu određenu arhitekturu, a da taj program može ispravno raditi i na nekim drugim arhitekturama.
OpenCL uključuje jezik (baziran na C99 jeziku) za pisanje kernela (funkcija koje se izvode na OpenCL uređajima) i API-je koji se koriste za kontrolu izvršavanja.
OpenCL se može koristiti kako bi aplikacijama omogućio pristup grafičkoj kartici radi izvršavanja različitih vrsta izračuna koji ne moraju nužno imati veze sa grafikom.

Programiranje u OpenCL-u

Tipovi podataka u OpenCL-u

Skalarni tipovi podataka

Vektorski tipovi podataka

N - Supported values of N are 2,4,8,16.

Tipovi objekata

PyOpenCL

Programiranje u PyOpenCL-u

Bitne funkcije OpenCL-a Bitne funkcije za kernele:

Primjer jednostavnog programa za PyOpenCL

import pyopencl as cl
import pyopencl.array as cl_array
import numpy
import numpy.linalg as la

a = numpy.random.rand(50000).astype(numpy.float32)
b = numpy.random.rand(50000).astype(numpy.float32)

ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)

a_dev = cl_array.to_device(queue, a)
b_dev = cl_array.to_device(queue, b)
dest_dev = cl_array.empty_like(a_dev)

prg = cl.Program(ctx, """
__kernel void sum(__global const float *a,
__global const float *b, __global float *c)
{
int gid = get_global_id(0);
c[gid] = a[gid] + b[gid];
}
""").build()

prg.sum(queue, a.shape, None, a_dev.data, b_dev.data, dest_dev.data)

print(la.norm((dest_dev - (a_dev+b_dev)).get())) 

Primjer nešto složenijeg programa - jednostavno sortiranje

# -*- coding: utf-8 -*-
import pyopencl as cl
import pyopencl.array as cl_array
import numpy
import numpy.linalg as la

a = numpy.random.rand(50000).astype(numpy.float32)

ctx = cl.create_some_context()
queue = cl.CommandQueue(ctx)

a_dev = cl_array.to_device(queue, a)
dest_dev = cl_array.empty_like(a_dev)

prg = cl.Program(ctx, """
__kernel void ParallelSelection(__global const float * in,__global float * out)
{
  int i = get_global_id(0);
  int n = get_global_size(0);
  float a = in[i];
  int pos = 0;
  for (int j = 0; j < n; j++)
  {
    float b = in[j];
    bool smaller = (b < a) || (b == a && j < i);
    pos += (smaller)?1:0;
  }
  out[pos] = a;
}
""").build()

prg.ParallelSelection(queue, a.shape, None, a_dev.data, dest_dev.data)

print dest_dev.get()[:100]

Transponiranje matrica

import pyopencl as cl
import numpy

block_size = 16

class Transpose1:
    def __init__(self, ctx):
        self.kernel = cl.Program(ctx, """
        __kernel void transpose(__global float *a_t, __global float *a, unsigned a_width, unsigned a_height)
        {
          int read_idx = get_global_id(0) + get_global_id(1) * a_width;
          int write_idx = get_global_id(1) + get_global_id(0) * a_height;

          a_t[write_idx] = a[read_idx];
        }
        """% {"block_size": block_size}).build().transpose

    def __call__(self, queue, tgt, src, shape):
        w, h = shape
        return self.kernel(queue, (w, h), (block_size, block_size), tgt, src, numpy.uint32(w), numpy.uint32(h))

class Transpose2(Transpose1):
    def __call__(self, queue, tgt, src, shape):
        w, h = shape

        return self.kernel(queue, (w, h), None, tgt, src, numpy.uint32(w), numpy.uint32(h))

def benchmark_transpose():
    ctx = cl.create_some_context()

    queue = cl.CommandQueue(ctx, properties=cl.command_queue_properties.PROFILING_ENABLE)

    sizes = [4096, 4448, 4864, 5280, 5792, 6304, 6880, 7488]
    mem_bandwidths = {}

    methods = [Transpose2, Transpose1]
    for klasa in methods:
        name = klasa.__name__
        mem_bandwidths[klasa] = meth_mem_bws = []

        for size in sizes:
            source = numpy.random.rand(size, size).astype(numpy.float32)

            mf = cl.mem_flags
            a_buf = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf = source)
            a_t_buf = cl.Buffer(ctx, mf.WRITE_ONLY, size = source.nbytes)
            method = klasa(ctx)

            events = []
            count = 4
            for i in range(count): # pozovi 4 puta za svaku velicinu
                events.append(method(queue, a_t_buf, a_buf, source.shape))

            events[-1].wait()
            time = sum(evt.profile.end - evt.profile.start for evt in events)

            mem_bw = 2*source.nbytes * count/(time*1e-9)
            print("benchmarking", name, size, mem_bw/1e9, "GB/s")

            a_buf.release()
            a_t_buf.release()

benchmark_transpose()

Primjeri primjene GPGPU-a

oclHashcat-plus

oclHashcat-plus je trenutno najbrži cracker za md5crypt, phpass, mscash2 i WPA / WPA2.

cRARk

cRARk je besplatan alat za otkrivanje šifri RAR i 7-Zip arhiva.
Usporedba razbijanja šifre .rar arhive sa alatom cRARk sa i bez korištenja grafičke kartice

crark_opencl
Prema slici možemo zaključiti da je brzina razbijanja šifre pomoću grafičke kartice jednaka 4430 isprobanih lozinki u sekundi.
crark
Prema slici možemo zaključiti da je brzina razbijanja šifre pomoću procesora jednaka 141 isprobanih lozinki u sekundi. To je oko 30 puta sporije od slučaja kada se koristi grafička kartica uz napomenu da ovo može varirati ovisno o grafičkoj kartici i procesoru.
http://developer.amd.com/wordpress/media/2012/10/opencl-1.2.pdf
OpenCL specification: http://www.khronos.org/registry/cl/specs/opencl-1.x-latest.pdf#page=168
http://sa10.idav.ucdavis.edu/
http://sa10.idav.ucdavis.edu/docs/sa10-Course-Introduction.pdf
http://www.gremedy.com/downloading.php?platform=windows32
http://developer.apple.com/library/mac/navigation/#section=Frameworks&topic=OpenCL
http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2964860/
http://www.multicoreinfo.com/research/slides/OpenCL-slides.pdf
OpenCL sorting: http://www.bealto.com/gpu-sorting_parallel-selection.html
GPU Performance: http://www.moderngpu.com/intro/performance.html

Izradio: Krunoslav Đuras