[CUDA] RC6 / Serpent / Twofish / AES / MARS

AW: [CUDA] RC6

Wie die Fehlermeldung sagt rufst du eine Host-Funktion von der __global__ Funktion auf. Du musst einfach vor alle Funktionen, die innerhalb der __global__ Funktion aufgerufen werden ein __device__ schreiben, um diese zu Device-Funktionen zu machen.

Im Mustercode fehlt nicht wirklich ein & sondern ist in diesem Fall optional, da Arrays zu Pointern zerfallen.
 
AW: [CUDA] RC6

Okay, so kriege ich das ganze zum Laufen und die Errors weg ... allerdings kommen dafür 18 Warnings. Ich hoffe, das ist nicht schlimm?!

snapshot11.png
 
AW: [CUDA] RC6

Die Funktionen die in den ersten beiden warnings genannt werden auch im header als __device__ deklarieren sollte die warnings beheben.
 
AW: [CUDA] RC6

Was meinst du? Im header steht doch "__device__ void rc6_enc(blabla) {"
 
AW: [CUDA] RC6

Da ich deinen geänderten Code nicht sehen kann, kann ich leider nur vermuten wo jetzt überall __device__ steht. Allerdings besagen die ersten beiden Warnungen, dass die Funktion rc6_end und rc6_dec erneut deklariert wurden allerdings jetzt mit __device__ statt wie vorher __host__. Offensichtlich sind also die Funktionen rc6_end und rc6_dec nicht einheitlich deklariert. Ich vermute also, du hast nur in der *.c Datei (oder *.cu für CUDA) überall __device__ davor geschrieben aber die *.h Datei (also den Header) vergessen. Kann ich ohne Code natürlich nur spekulieren aber danach sieht die Warnung aus.
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

In der .h Datei hatte ich testweise __device__ vor die beiden Funktionen geschrieben, aber dann sofort nach dem Kompilieren der .cu Datei wieder jede Menge Errors bekommen ...

snapshot12.png

Deswegen habe ich dann in der .h Datei auf "__device__" verzichtet und lebe lieber mit den Warnings.

Hier nochmal der gesamte Code:
Code:
#include <cuda_runtime.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "RC6.h"

#define P32 0xB7E15163
#define Q32 0x9E3779B9

uint32_t rotl32(uint32_t a, uint8_t n){
        n &= 0x1f;
        return ( (a<<n)| (a>>(32-n)) );
}
uint32_t rotr32(uint32_t a, uint8_t n){
        n &= 0x1f;
        return ( (a>>n)| (a<<(32-n)) );
}

uint8_t rc6_init(void* key, uint16_t keylength_b, rc6_ctx_t *s){
        return rc6_initl(key, keylength_b, 20, s);
}


uint8_t rc6_initl(void* key, uint16_t keylength_b, uint8_t rounds, rc6_ctx_t *s){
        uint8_t i,j;
        uint16_t v,p,c;
        uint32_t a,b, l=0;
        if (rounds>125)
                return 2;
        if(!(s->S=(uint32_t*)malloc((2*rounds+4)*sizeof(uint32_t))))
                return 1;

        s->rounds=rounds;

        c = keylength_b/32;
        if (keylength_b%32){
                ++c;
                j=(keylength_b%32)/8;
                if(keylength_b%8)
                        ++j;
                for (i=0; i<j; ++i)
                        ((uint8_t*)&l)[i] = ((uint8_t*)key)[(c-1)*4 + i];
        } else {
                l = ((uint32_t*)key)[c-1];
        }

        s->S[0] = P32;
        for(i=1; i<2*rounds+4; ++i){
                s->S[i] = s->S[i-1] + Q32;
        }

        a=b=j=i=0;
        v = 3 * ((c > 2*rounds+4)?c:(2*rounds+4));
        for(p=1; p<=v; ++p){
                a = s->S[i] = rotl32(s->S[i] + a + b, 3);
                if (j==c-1){
                        b = l = rotl32(l+a+b, a+b);
                } else {
                        b = ((uint32_t*)key)[j] = rotl32(((uint32_t*)key)[j]+a+b, a+b);
                }
                i = (i+1) % (2*rounds+4);
                j = (j+1) % c;
        }
        return 0;
}

void rc6_free(rc6_ctx_t *s){
        free(s->S);
}

#define LG_W 5
#define A (((uint32_t*)block)[0])
#define B (((uint32_t*)block)[1])
#define C (((uint32_t*)block)[2])
#define D (((uint32_t*)block)[3])

__device__ void rc6_enc(void* block, rc6_ctx_t *s) {
    uint8_t i;
    uint32_t t,u,x;

    B += s->S[0];
    D += s->S[1];

    for (i=1; i<=s->rounds; ++i){
        t = rotl32(B * (2*B+1), LG_W);
        u = rotl32(D * (2*D+1), LG_W);
        A = rotl32((A ^ t), u) + s->S[2*i];
        C = rotl32((C ^ u), t) + s->S[2*i+1];
        x = A;
        A = B;
        B = C;
        C = D;
        D = x;
    }
    A += s->S[2*s->rounds+2];
    C += s->S[2*s->rounds+3];
}

__device__ void rc6_dec(void* block, rc6_ctx_t *s) {
    uint8_t i;
    uint32_t t,u,x;

    C -= s->S[2*s->rounds+3];
    A -= s->S[2*s->rounds+2];

    for (i=s->rounds; i>0; --i){
        x=D;
        D=C;
        C=B;
        B=A;
        A=x;
        u = rotl32(D * (2*D+1), LG_W);
        t = rotl32(B * (2*B+1), LG_W);
        C = rotr32(C - s->S[2*i+1], t) ^ u;
        A = rotr32(A - s->S[2*i+0], u) ^ t;
    }
    D -= s->S[1];
    B -= s->S[0];
}

__global__ void rc6enc(char* block, rc6_ctx_t *s) {
    rc6_enc(block, s);
}

__global__ void rc6dec(char* block, rc6_ctx_t *s) {
    rc6_enc(block, s);
}

int main(void) {
    char* key = "meingeheimespasswort";
    rc6_ctx_t s_var;
    rc6_ctx_t *s;
    rc6_init(key, strlen(key), &s_var);
    char block_var[16] = {0};
    float elapsedTime=0.0;

    cudaMalloc(&s, sizeof(rc6_ctx_t));
    cudaMemcpy(s, &s_var, sizeof(s_var), cudaMemcpyHostToDevice);

    char* block;
    cudaMalloc(&block, sizeof(block_var));
    cudaMemcpy(block, &block_var, sizeof(block_var), cudaMemcpyHostToDevice);

    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);

    cudaEventRecord( start, 0 );

    rc6enc<<<1,1>>>(block, s);
    rc6dec<<<1,1>>>(block, s);

    cudaEventRecord( stop, 0 );
    cudaEventSynchronize( stop );

    cudaEventElapsedTime( &elapsedTime, start, stop );

    printf("time: %.4f ms\n", elapsedTime);

    cudaFree(s);
    cudaFree(block);

    return 0;
}
Code:
#pragma once

#ifndef RC6_H_
#define RC6_H_

#include <stdint.h>

typedef struct rc6_ctx_st{
        uint8_t         rounds;         /* specifys the number of rounds; default: 20 */
        uint32_t*       S;                      /* the round-keys */
} rc6_ctx_t;


uint8_t rc6_init(void* key, uint16_t keylength_b, rc6_ctx_t *s);

uint8_t rc6_initl(void* key, uint16_t keylength_b, uint8_t rounds, rc6_ctx_t *s);

__device__ void rc6_enc(void* block, rc6_ctx_t *s);
__device__ void rc6_dec(void* block, rc6_ctx_t *s);

void rc6_free(rc6_ctx_t *s);
#endif /* RC6_H_ */
 
AW: [CUDA] RC6

Auch vor die rotl32 und rotr32 Funktionen musst du __device__ schreiben. Generell alle Funktionen, die von der __global__ Funktion aus aufgerufen werden (und Funktionen, die von diesen Funktionen aufgerufen werden) müssen __device__ sein, da ja alles auf der GPU ausgeführt werden soll.
 
AW: [CUDA] RC6

Wenn ich vor alle Funktionen im .h File "__device__" schreibe, kann ich das ganze zumindest auf 3 Errors reduzieren ...

snapshot13.png
 
AW: [CUDA] RC6

Alle Funktionen hab ich gar nicht gesagt. Ich sagte alle Funktionen, die von deiner __global__ Funktion aus aufgerufen werden. Dazu zählen nicht rc6_init, rc6_initl und rc6_free, die du ja sogar vom Host Code aufrufst. Hab mir grade nochmal den Code angeguckt und die rotl32, rotr32 Funktionen werden sowohl vom Host Code (rc6_initl) und vom Device Code (rc6_enc/rc6_dec) aus aufgerufen. Solche Funktionen müssen mit beiden Keywords __host__ und __device__ versehen werden. Also nochmal zusammenfassend:

rc6_enc, rc6_enc: __device__
rc6_init, rc6_initl, rc6_free: __host__ (kann weggelassen werden da standardeinstellung)
rotl32, rotr32: __device__ __host__
 
AW: [CUDA] RC6

Jetzt sind die Errors weg, 6 Warnings bleiben ... aber das ist jetzt auch kein Weltuntergang. M. E. kann man's so lassen ... außer du weißt, wie ich die Warnings auch noch wegkriegen könnte. Für Variablen gibt's meines Wissens nach keine CUDA-spezifischen Wörter?! So weiß er halt nicht genau, auf welchen Speicher die Pointer zeigen und nimmt (glücklicherweise) autmatisch den GPU-Speicher (?) an.

snapshot14.png
 
AW: [CUDA] RC6

Ich denke die Warnungen können so bleiben, allerdings ist genau dieser S pointer im rc6_ctx_t struct der Teil den du noch manuell auf die GPU kopieren musst damit es dann funktioniert. Eventuell sind dann auch die Warnungen weg.
 
AW: [CUDA] RC6

Ich habe mich jetzt noch eine Weile mit dem Code beschäftigt, vor allem, weil ich ihn auch noch bestmöglich parallelisieren soll, aber was mir vorher noch aufgefallen ist: Ich bekomme immer nur "time: 0.0000 ms" als Ergebnis der Zeitmessung raus. Das verwundert mich trotz der geringen Datenmenge auch bei einer alten/schwachen 9600M GT doch.

Angenommen, char block_var[16] = "aaaaaaaaaaaaaaa", und ich verschlüssele das mit unserem RC6 Code, dann würde ich mir doch erwarten, dass das vielleicht 5 ms, oder irgendwas ähnliches in der Größenordnung dauert?!

Habe ich irgendeinen Fehler in der Zeitmessung? :huh:
Code:
int main(void) {
    char* key = "meingeheimespasswort";
    rc6_ctx_t s_var;
    rc6_ctx_t *s;
    rc6_init(key, strlen(key), &s_var);
    char block_var[16] = {0};

    cudaMalloc(&s, sizeof(rc6_ctx_t));
    cudaMemcpy(s, &s_var, sizeof(s_var), cudaMemcpyHostToDevice);

    char* block;
    cudaMalloc(&block, sizeof(block_var));
    cudaMemcpy(block, &block_var, sizeof(block_var), cudaMemcpyHostToDevice);

    [COLOR=royalblue][B]cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord( start, 0 );[/B]
    rc6enc<<<1,1>>>(block, s);
    rc6dec<<<1,1>>>(block, s);

    [COLOR=royalblue][B]cudaEventRecord( stop, 0 );
    cudaEventSynchronize( stop );
    float elapsedTime;
    cudaEventElapsedTime( &elapsedTime, start, stop );[/B]
    [COLOR=royalblue][B]printf("time: %.4f ms\n", elapsedTime);
    
    cudaEventDestroy( start );
    cudaEventDestroy( stop );[/B]
    cudaFree(s);
    cudaFree(block);

    return 0;
}
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

Der Code sieht OK aus. Das mit den 0ms scheint zwar unwahrscheinlich, aber nicht völlig ausgeschlossen. Ein paar Bytes hin und her schieben dauert nicht wirklich lange. Ich tippe aber eher darauf, dass eine der CUDA-Funktionen fehlgeschlagen ist. Um das zu überprüfen solltest du den Rückgabewert aller cuda... Funktionen überprüfen. Dazu verwende ich selbst folgende Helperfunction:

Code:
#define HandleError(x) HandleErrorImpl(x, __FILE__, __LINE__)

inline void HandleErrorImpl(cudaError error, const char* file, int line)
{
  if(error != cudaSuccess)
  {
    std::cerr << file << ":" << line << " " << cudaGetErrorString(error) << std::endl;
    exit(1);
  }
}

Falls du nur C und nicht C++ benutzt dann einfach den Kram mit cerr durch printf ersetzen und den Dateinamen, Zeilennummer und Fehlerbeschreibung anzeigt. Jetzt einfach noch um alle CUDA-Funktionen ein HandleError() drum packen, also sowas wie:

Code:
HandleError( cudaEventCreate(&start) );
HandleError( cudaEventCreate(&stop) );

Ist zwar etwas nervig, aber unverzichtbar bei der Fehlersuche. Nach einem Kernelaufruf mit der name<<<x,y>>> Syntax kann auch ein HandleError( cudaGetLastError() ); nicht schaden. Falls dann keine Fehler und immer noch 0ms angezeigt werden, dann dauert die ausführung wohl tatsächlich nur so lange.
 
AW: [CUDA] RC6

Irgendwas habe ich beim printf falsch gemacht, aber zumindest wissen wir jetzt, dass der Fehler beim synchronize in Zeile 161 liegt ...

snapshot16.png

[Edit]
Wenn ich den Error mit nem %s ausgeben lassen, heißt's "unspecified launch failure":

snapshot17.png
 
AW: [CUDA] RC6

Ja du musst das schon mit %s ausgeben. Die file variable und was cudaGetErrorString zurück gibt sind beides strings. Also sowas: printf("%s:%i %s", ...)

"unspecified launch failure" heißt wohl, dass irgendwas mit deiner Kernel Funktion nicht in Ordnung ist. Zugriffe auf Speicher, der nicht initialisiert ist oder sowas. Hast du eigentlich schon die Daten vom Pointer im rc6_ctx_t struct auch auf die GPU Kopiert? Sonst liegt es garantiert daran.
 
AW: [CUDA] RC6

Hab's jetzt mit %s %d %s ausgeben lassen: "../src/RC6.cu: 161 unspecified launch failure". Dass der Fehler im cu File ist, war eh klar. Nur was soll ich mir jetzt unter dem launch failure vorstellen?

[Edit]
rounds und *S? Nein?! Ich habe nur die cudaMemcpys, die s/s_var und block/block_var in den GPU Speicher kopieren ...
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

Hm ... wir haben doch eh schon für das ganze struct rc6_ctx_t Speicher allokiert?! Und unser s ist doch dieser Pointer?! Irgendwie verstehe ich gerade nicht, was ich noch machen soll. :huh:

Mich verwirrt das auch, dass wir einen eigenen char* key haben, obwohl doch in S die Rundenschlüssel gespeichert werden sollten?!
 
AW: [CUDA] RC6

Wenn du dir die rc6_initl Funktion nochmal anguckst, dann findest du dort folgende Zeile:
Code:
if(!(s->S=(uint32_t*)malloc((2*rounds+4)*sizeof(uint32_t))))
Diese Daten musst du auch auf die GPU kopieren. An den Parametern vom malloc siehst du wie groß der Speicherbereich ist. Um das ganze dann auf die GPU zu kopieren brauchst du dann etwa folgenden Code:

Code:
rc6_ctx_t temp = s_var;
cudaMalloc(&temp.S, (2*s_var.rounds+4)*sizeof(uint32_t));
cudaMemcpy(temp.S, s_var.S, (2*s_var.rounds+4)*sizeof(uint32_t), cudaMemcpyHostToDevice);

rc6_ctx_t* s;
cudaMalloc(&s, sizeof(temp));
cudaMemcpy(s, &temp, sizeof(temp), cudaMemcpyHostToDevice);
Code ungetestet

Edit: Wir haben zwar für das rc6_ctx_t struct Speicher reserviert aber wenn du dir das struct anguckst so stellst du fest, dass dieses struct wiederum einen Pointer auf einen Speicherbereich enthält. Diesen Speicherbereich muss man manuell kopieren, da das kopieren des Structs selbst nur den Pointer kopiert aber nicht die Daten wohin der Pointer zeigt.

Den char* key brauchen wir nur einmal für die rc6_init Funktion, die daraus dann die Rundenschlüssel generiert. Man hätte den Wert auch direkt an die rc6_init Funktion übergeben können aber dann müsste man die länge des Strings von Hand zählen. So kann man strlen benutzen.

Noch etwas Erklärung zu dem Beispielcode oben: Zuerst erstelle ich eine Kopie des s_var Structs und tausche den Pointer durch einen von CUDA reservieren Pointer aus. Danach werden die Daten vom ursprünglichen s_var Struct kopiert. Abschließend kopieren wir das ganze temp Struct auf die GPU.
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

*s hatten wir schon deklariert, ansonsten stimmt's jetzt mit dieser Code-Ergänzung. Die Zeit kommt mir auch schon vernünftiger vor:

snapshot20.png

Jetzt muss ich nur noch durchschauen, wofür diese ganzen Variablen und Pointer stehen. Bei S hatte ich angenommen, dass das der jeweilige Rundenschlüssel sei, aber warum haben wir dann einen eigenen char* key definiert? Und *s ist wohl einfach der Pointer auf S?!
Wozu brauchen wir eigentlich s_var? Das ist ne Variable an deren Adresse wir irgendwas speichern?! Die Daten, auf die der Pointer *s zeiget?! Das wäre ja dann der Rundenschlüssel?!

Und in tmp (bzw. temp bei dir) speichern wir nochmal s_var, da komme ich nicht ganz mit.

In block_var werden die Daten gespeichert, auf die der Pointer *block zeigt, oder wie?

[Edit]
Okay, jetzt verstehe ich's schon eher. Eine Frage noch, nur damit ich das richtig verstehe: Der aktuelle Code verschlüsselt 16 0er, oder (char block_var[16] = {0})? Andere Daten sehe ich sonst nirgends.
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

Ja das sieht ja schonmal gut aus. Zeile 145 und 146 kannst du übrigens streichen, da du jetzt zweimal Speicher reservierst für das s struct. Da du von den ganzen Pointern verwirrt zu sein scheinst hier nochmal zusammenfassend alles:

Erstmal musst du Pointer auf dem RAM und auf der GPU unterscheiden. Da beides nur gewöhnliche Pointer sind muss man aufpassen welcher Pointer im RAM oder auf der GPU liegt. Außerdem braucht man viele Pointer doppelt wenn man zwischen CPU und GPU Daten kopiert. Normalerweise schreibe ich ein h_ oder d_ für host bzw. device vor die Variablennamen um diese auseinander zu halten. Vor der Sache mit dem temp hatten wir folgende Variablen (fehlen eventuell einige unwichtige):

Host variablen: char* key, rc6_ctx_t s_var, char block_var[]
Device variablen: rc6_ctx_t *s, char* block;

Dabei ist s die GPU Variante von s_var und block die GPU Variante von block_var. Den key brauchen wir nur auf der CPU, da wir damit unser s_var initialisieren. An sich wäre alles recht überschaulich wenn das rc6_ctx_t Struct nicht selbst nochmal einen Pointer enthalten würde, wie hier zu sehen ist:
Code:
typedef struct rc6_ctx_st{
        uint8_t         rounds;         /* specifys the number of rounds; default: 20 */
        uint32_t*       S;                      /* the round-keys */
} rc6_ctx_t;
Wir müssen also auch noch dafür sorgen, dass die Daten wohin der S Pointer zeigt auch auf die GPU kopiert werden. Dazu erstelle ich erstmal eine Kopie von der s_var Variable und sorge dafür, dass der S Pointer auf Speicher in der GPU zeigt. Anschließend kopiere ich noch die daten von s_var.S nach temp.S womit ich dann die Rundenschlüssel schonmal auf der GPU habe. Anschließend fehlt nur noch, dass wir die ganze temp Variable auf die GPU kopieren. Diese Variable heißt momentan s. Das Kopieren von block_var nach block ist wieder einfach, da wir keine verschachtelten Pointer beachten müssen. Ich hoffe die erklärung hat dir geholfen und dich nicht noch mehr verwirrt.

Edit: Ja richtig. char block_var[16] = {0} enthält 16 Bytes die alle 0 sind. Du könntest aber auch irgendwas anderes schreiben wie char block_var[] = "mein toller text". Sollte nur mindestens 16 Zeichen lang sein, da dies der Blockgröße entspricht, die RC6 immer aufeinmal verschlüsselt.
 
Zuletzt bearbeitet:
Zurück