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

B

boss3D

Guest
Hi @ all!

Ich muss für ein Projekt im Studium mehrere Verschlüsselungs-Algorithmen in CUDA benchmarken. Wo ich die Algorithmen hernehme, ist dabei egal.

ATM arbeite ich daran, RC6 nach CUDA zu portieren. Einen C Code habe ich auf github gefunden, und gleich mal eine main Funktion dazugebastelt. Da ich nahezu 0 Erfahrungen mit CUDA habe und mir das jetzt selbst Schritt für Schritt beibringen darf, habe ich für den Anfang erstmal die "Idiotenlösung" gewählt, und einfach versucht, die beiden Haupt-(CPU)-Funktionen in 2 GPU-Funktionen aufzurufen. So sieht das ganze derzeit aus:
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;
        [COLOR=blue][B]if(!(s->S=malloc((2*rounds+4)*sizeof(uint32_t))))[/B]                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])

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];
}

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];
}

[COLOR=blue][B]__global__ void rc6enc() {
    rc6_enc();
}

__global__ void rc6dec() {
    rc6_enc();
}[/B]
int main(void) {
    float elapsedTime=0.0;

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

    cudaEventRecord( start, 0 );

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

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

    cudaEventElapsedTime( &elapsedTime, start, stop );

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

    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);

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

void rc6_free(rc6_ctx_t *s);
#endif /* RC6_H_ */
^^ Die blau markierten Zeilen sind zur Zeit das Problem. Bei den beiden Funktionen fehlen die Parameter, die ich den CPU-Funktionen übergeben muss, aber ich weiß nicht so recht, wie genau ich die da reinschreiben soll. Und bei der if-Zeile gibt's ein Pointer Problem, das sich mir auch noch nicht so ganz erschließt ... :huh:

snapshot7.png

Für jede Hilfe bin ich dankbar!
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

^^ Ja, das ist das Pointer-Problem in der if- bzw. malloc-Zeile ... Dort sehe ich den Fehler (im nicht selbst-geschriebenem Code!) noch nicht. Bei meinen beiden eingefügten GPU Funktionen weiß ich, dass jeweils 2 Parameter an die internen Funktionsaufrufe übergeben werden müssten, aber ich weiß noch nicht, wie genau ich "block" und "*s" da reinkriegen soll, sodass das auch funktioniert ... :huh:
 
AW: [CUDA] RC6

Ich denke, der fehler mit malloc sollte so zu lösen sein.

Verständlich:
Code:
s->S = (uint32_t *)malloc((2 * rounds + 4) * sizeof(uint32_t));
    if (!s->S)
        return 1;

Kurz:
Code:
if(!(s->S=(uint32_t*)malloc((2*rounds+4)*sizeof(uint32_t))))
     return 1;

malloc returned immer void*. Allerdings scheint s->S ein uint32_t* zu sein, daher schmeisst dein compiler einen error raus, weil es eine fehlerhafte pointer zuweisung ist. Einfach nach uint32_t* casten und gut ist. Für das nächste mal: http://www.cplusplus.com/reference/cstdlib/malloc/
Was ist jetzt dein zweiter Fehler? Das habe ich noch nicht ganz Verstanden.
 
AW: [CUDA] RC6

^^Ok, danke.

Das zweite Problem sind die Funktionsparameter. Die Host-Funktionen haben folgende Header:
Code:
void rc6_enc(void* block, rc6_ctx_t *s) {
Code:
void rc6_dec(void* block, rc6_ctx_t *s) {
Und ich habe mir zwei GPU Funktionen gemacht, in denen diese beiden Host-Funktionen aufgerufen werden. Jetzt muss ich aber dort irgendwie deren Parameter reinbringen. Und daran scheitert's noch ...

Irgendwie so:
Code:
__global__ void rc6enc() {
    rc6_enc(block, *s);
}
Nur genau so funktioniert's natürlich nicht. Ich bin mir auch nicht ganz sicher, ob diese Lösung wirklich das machen würde, was ich brauche: Dass dann der ganze Host Code wirklich von der GPU berechnet wird. :huh:
 
AW: [CUDA] RC6

Du kannst an die Device Funktionen auch einfach Parameter übergeben, allerdings wird hier mit Pointern gearbeitet und du kannst nicht einfach Pointer von der CPU an GPU Funktionen übergeben. Du musst zuerst auf der GPU Speicher reservieren, die Daten von der CPU dorthin kopieren und dann den Pointer auf den GPU Memory an die Funktion übergeben.

Außerdem wirst du so wie du es momentan planst vermutlich keinen Geschwindigkeitsvorteil von GPU gegenüber der CPU feststellen, da du nur einen Thread und einen Block auf der GPU startest. Die Geschwindigkeit der GPU ergibt sich vor allem aus der hohen Parallelität mit der selbst 1000 Threads parallel bearbeitet werden. Ob sich RC4 oder andere Verschlüsselungen parallelisieren lassen weiß ich auch nicht. Je nach eingesetztem Modus lassen sich wenigstens die einzelnen Blöcke parallel verschlüsseln und entschlüsseln.

Deinen Fragen nach schätze ich deine Programmiererfahrungen in C/C++ eher gering ein und vermute, dass du diese Aufgabe nicht in absehbarer Zeit vernünftig gelöst kriegst. Ich würde mich eher nach einer bereits für CUDA angepassten Bibliothek umsehen. Eine Anpassung an CUDA ist nicht mal eben damit getan an ein paar Funktionen das __global__ Keyword dran zu schreiben.
 
AW: [CUDA] RC6

Dass es bei CUDA vor allem um Parallelisierung geht, ist mir klar, allerdings ist Performance (noch) nicht das Primärziel. Das wichtigste ist im Moment, überhaupt erstmal einen RC6 Code zu kriegen, der auf der GPU läuft. Optimiert, soweit dann möglich, wird danach.
Dass ich die Anzahl der Thread über die Parameter der aufrufenden Funktion angebe, weiß ich auch. Das ist ja die zweite Zahl in den spitzen Klammern, wenn ich das jetzt richtig in Erinnerung habe.

C-Kenntnisse hätte ich eigentlich schon (mal gehabt), allerdings im ersten Semester meines Studiums. Jetzt bin ich ein gutes Stück weiter und hatte schon seit fast 1 Jahr überhaupt gar nichts mehr mit C zu tun. Man vergisst das doch erstaunlich schnell, wenn man sich nicht damit befasst. Jetzt fürs Projekt muss ich mich aber wieder damit beschäftigen.

Ich schau mir das jetzt erstmal mit dem GPU Memory an, allerdings weiß ich noch nicht so wirklich, was genau ich von der CPU dort hin kopieren soll. Was genau ist das/der "block"? Oder Pointer *s? Muss ich mir noch anschauen ...
^^ Wenn ich das mit den beiden Parametern noch schaffe, müsste das ganze m. E. schon auf der GPU lauffähig sein. Dass es eine Idiotenlösung ist, den ganzen CPU Code quasi in eine __global__ Funktion "einzupacken", habe ich ja gesagt, aber fürs erste geht's wirklich mal nur um Lauffähigkeit.
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

Der rc6_ctx_t *s Pointer speichert wohl einen internen Zustand, wie die verwendeten Schlüssel und Anzahl der Runden. Der void* block Pointer enthält einen Block der zu verschlüsselnden Daten. Scheinbar ist dieser bei RC6 immer 16 Bytes groß. Beides musst du vorm Aufruf von deiner rc6enc/dec Funktion in den GPU Memory kopieren. Der void* block Pointer enthält danach die verschlüsselten Daten, also den danach wieder zum Host kopieren falls du an den Daten interessiert bist.

Zu beachten ist auf jeden Fall, dass du im Host Code nirgendwo einen Pointer, der mit cudaMalloc reserviert wurde, dereferenzierst. Du kannst die Daten im GPU Memory nicht direkt verändern und musst den Umweg über das Kopieren vom Host Memory mithilfe von cudaMemcpy gehen (oder eine GPU Funktion aufrufen, die die Daten initialisiert).
 
Wenn du nir mit der parallelisierung rumexperimentieren willst ohne dich in Cuda einarbeiten zu müssen, ist Java aparapi oder C++ AMP ganz nett.
 
AW: [CUDA] RC6

So, ich habe das jetzt mal umzusetzen versucht, und ich denke, die ungefähre Richtung stimmt?! Ein paar Fehler habe ich noch in der Umsetzung:
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])

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];
}

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];
}

[COLOR=blue][B]__global__ void rc6enc(void* block, rc6_ctx_t *s) {
    rc6_enc(block, s);
}

__global__ void rc6dec(void* block, rc6_ctx_t *s) {
    rc6_enc(block, s);
}[/B]
int main(void) {
    rc6_ctx_t s_var;
    void block_var;
    float elapsedTime=0.0;

    [COLOR=blue][B]rc6_ctx_t *s;
    cudaMalloc((void**)&s, sizeof(rc6_ctx_t));
    void *block;
    cudaMalloc((void**)&block, sizeof(void));[/B]
    [COLOR=blue][B]cudaMemcpy(s_var, sizeof(rc6_ctx_t), cudaMemcpyHostToDevice);
    cudaMemcpy(block_var, sizeof(void), cudaMemcpyHostToDevice);[/B]
    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);

    cudaEventRecord( start, 0 );

    [COLOR=blue][B]rc6enc<<<1,1>>>(block, s);
    rc6dec<<<1,1>>>(block, s);[/B]
    cudaEventRecord( stop, 0 );
    cudaEventSynchronize( stop );

    cudaEventElapsedTime( &elapsedTime, start, stop );

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

    [COLOR=blue][B]cudaFree(s);
    cudaFree(block);[/B]
    return 0;
}
snapshot9.png

^^ Am void block_var scheitert's jetzt noch. Da meint er was von wegen incomplete type. :huh:
Natürlich ist void Blödsinn, aber die selbe Fehlermeldung kommt auch bei anderen Datentypen.

BTW: Was das für Daten sind, die durch den Algorithmus fließen, spielt überhaupt keine Rolle. Es geht wirklich NUR um die Performance beim Ver- und Entschlüsseln (nachdem das ganze überhaupt erstmal unter CUDA läuft, woran ich gerade arbeite). Wie die Daten danach aussehen, ist egal, allerdings muss es natürlich eine korrekte Ver- bzw. Entschlüsselung bleiben. Den eigentlichen Code darf ich nicht verpfuschen, nur um ihn lauffähig zu kriegen.
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

Ähm wieso soll dein block_var denn vom typ void sein? Du weißt dass void für "nichts" steht oder? Was soll das speichern? Meinst du void* ? Aber selbst das wäre nicht zu empfehlen...
 
AW: [CUDA] RC6

Habe ich ja geschrieben, dass void Blödsinn ist. Es gibt keine "nichts-Variablen", aber ich weiß (noch) nicht, was ich sonst aus block bzw. block_var machen soll. Ich habe mich erstmal nur daran orientiert, dass es halt im host-Funktions-Kopf mit void* angegeben ist ...
 
AW: [CUDA] RC6

Hat keiner einer Idee, welchen Datentyp ich statt void für die block Variable(n) nehmen könnte? Ich habe schon die einfachen Datentypen durchprobiert, aber jedes Mal heißt's "incomplete type is not allowed" ... :huh:
 
AW: [CUDA] RC6

WTF...
du musst doch selber wissen, was du da überhaupt speichern möchtest? Wenn du so fragst, machst du den Eindruck als wenn du dir Code zusammenkopiert hast. Außerdem sollte cudaMemcpy doch 4 Parameter haben und nicht 3? NVIDIA CUDA Library: cudaMemcpy
Und dann musst du auch die Adresse von z.B. s_var übergeben und nicht einfach nur s_var...
Also entweder ich habe dein Geheimnis nicht verstanden oder du kopierst dir nur Code ohne den Sinn überhaupt im Ansatz zu verstehen...
 
AW: [CUDA] RC6

Der Code ist NICHT von mir! (siehe Startpost)
Ich habe mir einen C-COde von RC6 gegoogelt und muss den unter CUDA lauffähig machen ...

Es ist auch völlig egal, welche Art von Daten durch den RC6 laufen, solange der RC6 ansich das richtige damit macht. Ich habe die Frage mit den Datentypen eher so gemeint, dass keiner funktioniert, den ich bis jetzt ausprobiert habe (int, char, Felder davon, ...). K. A. welcher Datentyp gehen würde. DAS wollte ich eigentlich wissen.

Und die Adresse von s_var wird doch eh übergeben?! &s_var.
^^ In Zeile 139 wird ja auch kein Fehler gemeldet.
 
AW: [CUDA] RC6

Also ich hatte ja schon geschrieben, dass ein RC6 Block immer 16 Bytes groß ist also würde sich ein char Array mit 16 Elementen eignen. Da beliebige Pointer zu void* konvertiert werden in C kannst du das auch einfach so an die Funktion übergeben. Außerdem noch einige weitere Punkte die mir aufgefallen sind:
- Die Konvertierung zu (void**) bei cudaMalloc ist eigentlich unnötig
- Du nutzt cudaMemcpy falsch. cudaMemcpy hat 4 Argumente und nicht 3. Richtig würde ein Aufruf etwa so aussehen: cudaMemcpy(s, &s_var, sizeof(rc6_ctx_t), cudaMemcpyHostToDevice);
- Dir fehlt die Initialisierung von s_var. Dies muss passieren bevor du die Daten zur Grafikkarte kopierst. Das initialisieren übernimmt die rc6_init Funktion.

So in etwa würde ich die main Funktion schreiben (ungetestet):

Code:
char* key = "meingeheimespasswort";
rc6_ctx_t s_var;
rc6_init(key, strlen(key), &s_var); // ich hoffe strlen ist richtig und die abschließende '\0' wird nicht mitgezählt 

char block_var[16] = {0}; // block_var eventuell mit richtigen daten füllen

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

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

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

cudaFree(s);
cudaFree(block);
Ich würde dir in jedem Fall dazu raten deine C-Kenntnisse etwas aufzufrischen, denn deine Versuche sehen wie relativ planloses ausprobieren aus. Richtiger Umgang mit Pointern sollte man für dieses Vorhaben schon mitbringen.

Edit: Ich seh grade, dass das rc6_ctx_t struct auch noch Pointer enthält. Du musst für diese Daten also auch noch Platz auf der GPU reservieren und dorthin kopieren.
 
Zuletzt bearbeitet:
AW: [CUDA] RC6

^^ Ok, danke. Ich probiere das mal aus. "Herumprobieren" ist es leider wirklich, aber nach 1 Jahr kein C und dafür einem halben Jahr Java (keine pointer!) sieht das erstmal alles ein bisschen fremd aus ...

Wenn das mit char und char Arrays funktioniert, muss ich das halt dann auch in den anderen Algorithmen (AES, Serpent, Twofish, MARS) beachten, weil ich die ja gegeneinander benchmarken muss. Das macht ja nur Sinn, wenn sie den exakt selben Input verarbeiten.
 
AW: [CUDA] RC6

Wie grade im edit schon geschrieben habe ich noch nicht beachtet, dass rc6_ctx_t auch Pointer enthält. Ich kann dir also so schon garantieren, dass das Programm schön abstürzen wird wenn es denn so compiliert. Wenn du das aber berücksichtigst müsste es grundlegend erstmal funktionieren.
 
AW: [CUDA] RC6

Die main müsste jetzt stimmen, aber dafür habe ich 2 Errors gekriegt, die ich zuvor nicht hatte (siehe Screenshot in Post #11):

snapshot10.png

(Dir fehlt in deinem Mustercode ein "&" im zweiten cudaMemcpy vor Parameter 2)
 
Zurück