#include<stdlib.h>
#include<string.h>
#include <time.h>
#include "cuda.h"
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#define EINGABELAENGE 8
//Da vom Kernelcode keine Hostmethoden aufgerufen werden können werden alle Methoden
//die im Kernel gebraucht werden kopiert und als __Device__ Methoden deklariert
//__device__ methoden können nur von anderen __device__ methoden oder vom Kernel //aufgerufen werden
__device__
char *cudaStrcpy(char *dest, const char *src)
{
unsigned i;
for (i=0; src[i] != '\0'; ++i)
dest[i] = src[i];
dest[i] = '\0';
return dest;
}
__device__
int cudaStrcmp(char *gesamtString, char *gesWort, int intervall)
{
int i;
for (i = 0; gesamtString[i] == gesWort[i]; i++)
if (i == intervall - 1 && gesWort[i + 1] == '\0')
return 0;
return 1;
}
__device__
long cudaPow(int z, int h)
{
int i;
long temp = 1;
for(i = 0; i < h; i++)
{
temp *=z;
}
return temp;
}
long pow(int z, int h)
{
int i;
long temp = 1;
for(i = 0; i < h; i++)
{
temp *=z;
}
return temp;
}
//Kernel = Devicecode der vom Host aufgerufen wird, wird als __global__ deklariert
__global__
void BruteForce(char* charset, int wordlength, int charsetlenght, long* outp,
char* suche, char* s, long* d, int interval,
int offset, int round)
{
//tid ist die ThreadID da die Threads in CUDA mehrdimensional angegeben werden müssen //sie zuerst liniear gemacht werden
int tid = blockIdx.x * blockDim.x + threadIdx.x;
//Startwert und Endwert wird berechnet mit ThreadID * Interval
//für spätere Durchgänge, die mehr als 5 sekunden Rechenzeit bräuchten, muss die
//Aufgabe in kleinere Teile geteilt werden was mithilfe round und offset geschieht
long startw = round * offset + tid * interval;
long endw = round * offset + (tid + 1) * interval;
int i = 0;
long mw = 0;
long w = 0;
while(startw < endw)
{
//Die logik die aus Zahlen Buchstabenketten macht
mw = startw;
for(i = wordlength; i >= 0; i--)
{
w = (int)(mw / d[i]);
if ( i == wordlength)
{
if (w != 0)
{
s[tid * wordlength + i]=charset[w];
}
}
else
{
s[tid * wordlength + i] = charset[w];
}
mw = mw - w * d[i];
}
//Überprüft ob die aktuelle Buchstabenfolge die gesuchte ist
if(cudaStrcmp(&s[tid * wordlength],suche, wordlength) == 0)
{
outp[tid] = startw;
}
startw++;
}
}
// gibt den Startzeitpunkt und Endzeitpunkt aus
int printTime(tm startTime, tm endTime)
{
printf("\n\n%d:%d\n",startTime.tm_min,startTime.tm_sec);
printf("%d:%d\n",endTime.tm_min,endTime.tm_sec);
return 0;
}
//Überprüft ob ein Thread ein ergebnis zurückliefert
int checkResults(long* results, int resultCount)
{
int c1 = 0;
for(c1 = 0; c1 < resultCount; c1++)
{
if(results[c1] != -1)
{
printf("\n%d:%d",c1,results[c1]);
return results[c1];
}
}
return -1;
}
int main(int argc, char** argv)
{
time_t rawtime;
struct tm * timeinfo;
struct tm tmstart;
struct tm tmend;
int gefunden = -1;
int teiler = 0;
int offset = 0;
//h_ vor dem Variablenname bedeutet das es sich um eine hostvariable handelt
long * h_outp;
int h_i = 0;
int h_alphabetlaenge = 26;
char h_loweralpha[27] = "abcdefghijklmnopqrstuvwxyz";
char* h_eingabe;
long* h_d;
int h_interval;
int h_threadanzahl;
int c1 = 0;
h_eingabe = (char*)malloc(sizeof(char)*EINGABELAENGE);
h_outp = (long*)malloc(sizeof(long) * 26);
//d_ vor dem Variablenname bedeutet das es sich um eine devicevariable handelt
long* d_outp;
char* d_loweralpha;
char* d_eingabe;
char* d_s;
long* d_d;
//Speicherzuweisung von devicespeicher für die Variablen die bei jeden durchgang
//gleich bleiben
cudaMalloc((void **) &d_eingabe,sizeof(char)*EINGABELAENGE);
cudaMalloc((void **) &d_loweralpha,sizeof(char)*h_alphabetlaenge);
cudaMalloc((void **) &d_outp,sizeof(long) * 26);
//Das zu suchende Wort wird eingegeben
scanf("%s",h_eingabe);
//Der Startzeitpunkt wird festgelegt und in tmstart gespeichert
time ( &rawtime );
timeinfo = localtime ( &rawtime );
tmstart = *timeinfo;
//Die Eingabe und das Alphabet wird auf devicespeicher kopiert
cudaMemcpy(d_eingabe,h_eingabe,EINGABELAENGE,cudaMemcpyHostToDevice);
cudaMemcpy(d_loweralpha,h_loweralpha,h_alphabetlaenge,cudaMemcpyHostToDevice);
//Die schleife läuft solange bis ein ergebnis gefunden wurde oder bis EINGABELAENGE
//erreicht ist wobei EINGABELAENGE sich auf die maximale und nicht auf die laenge der
// aktuellen eingabe bezieht (siehe macro definition)
for (h_i = 1; h_i < EINGABELAENGE && gefunden == -1; h_i++)
{
printf("\n\n");
//Es wird der Interval und die Threadanzahl bestimmt da es beim ersten durchgang
//nur soviele Durchläufe gibt wie das alphabet lang ist werden nicht mehr
//threads gebraucht
if(h_i == 1)
{
h_interval = 1;
h_threadanzahl = h_alphabetlaenge;
}
else
{
//die optimale Anzahl der Threads ist für jede Grafikkarte anders
//und lässt sich über CUDA devicequerry herausfinden
h_threadanzahl = 4 * 112;
h_interval = (pow(h_alphabetlaenge,h_i)/h_threadanzahl);
}
//Jeder thread schreibt in output rein ob und an welcher stelle er den gesuchten
// String gefunden hatt
h_outp = (long*)malloc(sizeof(long) * h_threadanzahl);
cudaMalloc((void **) &d_outp,sizeof(long) * h_threadanzahl);
//outp wird auf -1 initialisiert weil die werte die zurückgegeben werden
//theoretisch zwischen 0 und unendlich liegen können
for(c1 = 0; c1 < h_threadanzahl; c1++)
h_outp[c1] = -1;
//die Werte von h_outp(-1) werden auf devicememory kopiert
cudaMemcpy(d_outp,h_outp,sizeof(long) * h_threadanzahl,cudaMemcpyHostToDevice);
//in d wird der Teiler berechnet die zur berechnung der strings im
//Bruteforce algorithmus gebraucht werden
cudaMalloc((void **) &d_d,sizeof(long) * h_i);
//in “s“ werden die strings gespeichert die gerade überprüft werden
//da diese mit jedem durchlauf(i) um eine stelle größer werden und
//jeder thread seinen eigenen speicher braucht muss entsprechend
//viel speicher allokiert werden
cudaMalloc((void **) &d_s,sizeof(char*) * h_threadanzahl * h_i);
//die teiler werden berechnet
h_d = (long*)malloc(sizeof(long) * h_i);
for(c1 = h_i; c1 >= 0; c1--)
{
h_d[c1]= pow(h_alphabetlaenge,c1);
}
//und auf das device kopiert
cudaMemcpy(d_d, h_d, h_i * sizeof(int),cudaMemcpyHostToDevice);
//Diese if/else anweisung ist ein workaround um den „5 sekunden bug“ zu beheben
//wenn eine Kernelfunktion länger als ca. 5 sekunden zum abarbeiten braucht
//wird diese vom Windows Treiber unterbrochen
if(h_i > 4)
{
//das passwort länger als 4 zeichen ist muss die aufgabe geteilt werden
//dadurch das der aufwand exponentiell steigt steigt auch die anzahl
//der benötigten teilungen exponentiell
//diese aufteilung führt dazu das der kernel immer gleichlang ausgeführt wird
teiler = 2 * pow(h_alphabetlaenge,h_i-5);
h_interval = (pow(h_alphabetlaenge,h_i)/h_threadanzahl)/teiler + 1;
offset = pow(h_alphabetlaenge ,h_i)/teiler;
for(c1 = 0; c1 < teiler; c1++)
{
//kernelaufruf 4 ist die anzahl der blocks und treadanzahl/4 die anzahl
//der Threads pro block
BruteForce<<<4,h_threadanzahl/4>>>(d_loweralpha, h_i, h_alphabetlaenge,
d_outp, d_eingabe, d_s, d_d, h_interval, offset, c1);
printf(".");
//zwischenergebnis wird aus dem device kopiert
cudaMemcpy(h_outp,d_outp,sizeof(long)*
h_threadanzahl,cudaMemcpyDeviceToHost);
//und überprüft
gefunden = checkResults(h_outp,h_threadanzahl);
//falls ein ergebnis vorliegt wird die endzeit angezeigt
if(gefunden != -1)
{
time ( &rawtime );
timeinfo = localtime ( &rawtime );
tmend = *timeinfo;
printTime(tmstart,tmend);
break;
}
}
}
else
{
//wenn die wortlänge kleiner als 5 ist reicht ein kernellaufruf
//zum berechnen
offset = 0;
c1 = 0;
BruteForce<<<4,h_threadanzahl/4>>>(d_loweralpha, h_i, h_alphabetlaenge,
d_outp, d_eingabe, d_s, d_d, h_interval, offset, c1);
cudaMemcpy(h_outp,d_outp,sizeof(long) *
h_threadanzahl,cudaMemcpyDeviceToHost);
gefunden = checkResults(h_outp,h_threadanzahl);
if(gefunden != -1)
{
time ( &rawtime );
timeinfo = localtime ( &rawtime );
tmend = *timeinfo;
printTime(tmstart,tmend);
break;
}
// . geben die anzahl der ausgeführten kernelaufrufe an
printf(".");
}
}
free(h_outp);
free(h_eingabe);
cudaFree(d_d);
cudaFree(d_s);
cudaFree(d_outp);
cudaFree(d_loweralpha);
cudaFree(d_eingabe);
cudaFree(d_s);
system("PAUSE");
return 0;
}
Jeder “.“ steht für einen abgeschlossenen Kernelaufruf, jede neue Zeile für eine neue Wortlänge die überprüft wird. 86 gibt die ThreadID des Threads an der den Treffer gelandet hat und 268478924 sind die Anzahl der versuche die es gebraucht hatt das Ergebnis zu finden.