lunedì 16 maggio 2016

Fractint per Arduino

Questo post prende spunto da Fractint, un software ormai molto datato (e' uno dei programmi open source che da piu' tempo e' sempre sviluppato) che permetteva il calcolo "veloce" di frattali nei calcolatori che erano privi di coprocessore matematico utilizzando gli interi per calcoli in virgola mobile.



Voluto vedere se riuscivo a fare qualcosa di simile con Arduino. Per fare cio' ho utilizzato la libreria AVRFix , libreria appositamente studiata per processori ATMEL

Il programma senza nessun tipo di ottimizzazione ha mostrato un tempo di esecuzione pari a 7581 ms. A parita' di condizioni di schermo la libreria AVRFix con il tipo di variabile short ha quasi dimezzato il tempo di calcolo per un totale di 4434 ms

Ovviamente questa non e' una soluzione generale. Basta infatti cambiare il tipo di varibiale lasciando intatto il codice per usa AVRFix che le prestazioni decadono rapidamente a 10590 ms per il tipo di varibile l a 18464 ms per il tipo di variabile ll

No AVRFix (7581 ms)
-----------------------------------------
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);



#define SCREEN_WIDTH 32
#define SCREEN_HEIGHT 128

#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//float re_min = -2.019531;
float a = -2.003906;
float im_min = -1.275;
float re_factor = 0.019531;
float im_factor = 0.075;

unsigned long tempo;

float b;
float x,y,x_new,y_new;

int k,j,i;

void setup()   {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  
  display.display();
  delay(100);
  display.clearDisplay();

  tempo = millis();
  
  for (i=0;i<SCREEN_HEIGHT;i++)
     {
     a = a + re_factor;
     b = im_min;
     for (j=0;j<SCREEN_WIDTH;j++)
      {
        b=b+im_factor;
        x = 0;
        y = 0;
      
        for (k=0;k<=64;k++)
            {
            x_new = (x*x)-(y*y)+a;
            y_new = (2*x*y)+b;
            if (((x_new*x_new)+(y_new*y_new))>4)
                {
                display.drawPixel(i, j, WHITE);
                //
                break;
                }
       x = x_new;
       y = y_new;
       } 
      }
     }
Serial.println(millis()-tempo);
display.display();
}


void loop() {
}
-----------------------------------------

AVRFix (tipo short 4728 ms)
-----------------------------------------
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);
extern "C"{
  #include <avrfix.h>
}

#define WIDTH 32
#define HEIGHT 128
lfix_t Im = ftosk(-1.275);
lfix_t DRe = ftosk(0.02);
lfix_t DIm = ftosk(0.075);
lfix_t A = ftosk(-2.0);
lfix_t B = ftosk(0.0);
lfix_t DUE = ftosk(2.0);
lfix_t X,Y;
lfix_t XN,YN,YNN;
lfix_t tt;
int j,k,i;
float test;

unsigned long tempo;

void setup() {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  
  display.display();
  delay(100);
  display.clearDisplay();
  tempo = millis();
  
  for (i=0;i<HEIGHT;i++)  
    {
      A=A+DRe;
      B=Im;
      for (j=0;j<WIDTH;j++)
        {
          B=B+DIm;
          X = ftosk(0.0); 
          Y = ftosk(0.0);
          for (k=0;k<=64;k++)
              {
               XN = smulsk(X, X)-smulsk(Y, Y)+A;
               YN = smulsk(X, Y);
               YNN = smulsk(X, Y);
               YN = smulsk(DUE, YNN) + B;
               tt = smulsk(XN,XN)+smulsk(YN,YN);
               test = sktof(tt);
               if (test > 4.0)
                    {
                        display.drawPixel(i, j, WHITE);
                        break;
                    }
               X=XN;
               Y=YN;
              }
        }
    }
  

  Serial.println(millis()-tempo);
  display.display();
  
}

void loop() {
}
-----------------------------------------

AVRFix (tipo l 10590 ms)
-----------------------------------------
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);
extern "C"{
  #include <avrfix.h>
}

#define WIDTH 32
#define HEIGHT 128
//lfix_t Re = ftolk(-2.00390625); 
lfix_t Im = ftosk(-1.275);
lfix_t DRe = ftok(0.019531);
lfix_t DIm = ftok(0.075);
lfix_t A = ftok(-2.00390625);
lfix_t B = ftok(0.0);
lfix_t DUE = ftok(2.0);
lfix_t X,Y;
lfix_t XN,YN,YNN;
lfix_t tt;
int j,k,i;
double test;

unsigned long tempo;

void setup() {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  
  display.display();
  delay(100);
  display.clearDisplay();
  tempo = millis();
  
  for (i=0;i<HEIGHT;i++)  
    {
      A=A+DRe;
      B=Im;
      for (j=0;j<WIDTH;j++)
        {
          B=B+DIm;
          X = ftok(0.0); 
          Y = ftok(0.0);
          for (k=0;k<=64;k++)
              {
               XN = mulk(X, X)-mulk(Y, Y)+A;
               YN = mulk(X, Y);
               YNN = mulk(X, Y);
               YN = mulk(DUE, YNN) + B;
               tt = mulk(XN,XN)+mulk(YN,YN);
               test = ktod(tt);
               if (test > 4.0)
                    {
                        display.drawPixel(i, j, WHITE);
                        break;
                    }
               X=XN;
               Y=YN;
              }
        }
    }
  

  Serial.println(millis()-tempo);
  display.display();
  
}

void loop() {

}
-----------------------------------------

AVRFix (tipo ll 18464 ms)
-----------------------------------------
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);
extern "C"{
  #include <avrfix.h>
}

#define WIDTH 32
#define HEIGHT 128
//lfix_t Re = ftolk(-2.00390625); 
lfix_t Im = ftolk(-1.275);
lfix_t DRe = ftolk(0.019531);
lfix_t DIm = ftolk(0.075);
lfix_t A = ftolk(-2.00390625);
lfix_t B = ftolk(0.0);
lfix_t DUE = ftolk(2.0);
lfix_t X,Y;
lfix_t XN,YN,YNN;
lfix_t tt;
int j,k,i;
double test;

unsigned long tempo;

void setup() {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  
  display.display();
  delay(100);
  display.clearDisplay();
  tempo = millis();
  
  for (i=0;i<HEIGHT;i++)  
    {
      A=A+DRe;
      B=Im;
      for (j=0;j<WIDTH;j++)
        {
          B=B+DIm;
          X = ftolk(0.0); 
          Y = ftolk(0.0);
          for (k=0;k<=64;k++)
              {
               XN = lmullk(X, X)-lmullk(Y, Y)+A;
               YN = lmullk(X, Y);
               YNN = lmullk(X, Y);
               YN = lmullk(DUE, YNN) + B;
               tt = lmullk(XN,XN)+lmullk(YN,YN);
               test = lktod(tt);
               if (test > 4.0)
                    {
                        display.drawPixel(i, j, WHITE);
                        
                        break;
                    }
               X=XN;
               Y=YN;
              }
        }
    }
  

  Serial.println(millis()-tempo);
  display.display();
  
}

void loop() {

}
-----------------------------------------


Disclaimer: il titolo di questo post non e' da intendersi come un plagio verso il nome del piu' noto software Fractint ma piuttosto come un tributo ad un programma che usavo sui 386 nei primi anni 90