Dopo il test di Cuda e' arrivato il momento di Metal, il linguaggio per la GPU di Apple
Gli ersempi in C++ sono un po' complicati per una semplice prova ed ho optato per la versione in Python in cui il kernel e' comunque scritto in C++ MetalCompute (negli esempi della libreria c'e' gia' una implementazione di Mandelbrot, l'ho scritta da zero per imparare...sicuramente la mia versione e' meno efficiente)
La logica seguita e' quella di Cuda, ogni thread si occupa di una riga
Si puo' solo passare un solo parametro come array di float al kernel e si ha in uscita un solo array di float. L'array di input e' generato con NumPy con le coordinate X,Y dei punti dell'insieme
import metalcompute as mc
from PIL import Image as im
mc.init()
code = """
#include <metal_stdlib>
using namespace metal;
kernel void mandelbrot(
const device float* arr [[ buffer(0) ]],
device float* out [[ buffer(1) ]],
uint id [[ thread_position_in_grid ]]) {
float a = arr[id];
float b;
float x_new,y_new,x,y;
int iterazioni = 255;
for (int s=0; s<640;s++)
{
x=0.0;
y=0.0;
b = arr[640+s];
for (int k=0;k<iterazioni;k++)
{
x_new = (x*x)-(y*y)+a;
y_new = (2.0*x*y)+b;
if (((x_new*x_new)+(y_new*y_new))>4)
{
out[id*640+s] = (float)(k%2)*255;
k=iterazioni;
}
x = x_new;
y = y_new;
}
}
}
"""
mc.compile(code, "mandelbrot")
import numpy as np
from time import time as now
dimensioni = 640
x_start = -2.0
x_stop = 0.75
x_res = (x_stop-x_start)/dimensioni
x = np.arange(start=x_start, stop=x_stop,step = x_res,dtype='f')
y_start = -1.5
y_stop = 1.5
y_res = (y_stop-y_start)/dimensioni
y = np.arange(start=y_start, stop=y_stop,step = y_res,dtype='f')
arr = np.concatenate((x,y))
print(arr.shape)
out_buf = np.empty(dimensioni*dimensioni,dtype='f')
start = now()
mc.run(arr, out_buf, dimensioni)
end = now()
immagine = out_buf.reshape(dimensioni,dimensioni)
print(end-start)
immagine = np.int_(immagine)
np.savetxt("dati.txt",immagine.astype(int),fmt='%i')
img = im.fromarray(np.uint8(immagine) , 'L')
img.save('m1.png')
img.show()