2016-04-27 4 views
2

Ich arbeite gerade an einem Programm, das eine 2D-FFT implementieren muss, (für Kreuzkorrelation). Ich habe eine 1D FFT mit CUDA gemacht, die mir die richtigen Ergebnisse lieferte, ich versuche jetzt eine 2D Version zu implementieren. Mit wenigen Beispielen und Dokumentation online finde ich es schwierig herauszufinden, was der Fehler ist.CUDA cammt 2D Beispiel

Bisher habe ich nur das cuFFT-Handbuch verwendet.

Wie auch immer, ich habe zwei 5x5 Arrays erstellt und sie mit 1 gefüllt. Ich habe sie auf den GPU-Speicher kopiert und die Vorwärts-FFT gemacht, sie multipliziert und dann auf dem Ergebnis ift gemacht. Dies ergibt ein 5x5-Array mit Werten von 650. Ich würde erwarten, ein Gleichstromsignal mit dem Wert 25 in nur einem Schlitz in dem 5x5-Array zu erhalten. Stattdessen bekomme ich 650 im gesamten Array.

Außerdem darf ich den Wert des Signals nicht ausdrucken, nachdem es auf den GPU-Speicher kopiert wurde. Schreiben

cout << d_signal[1].x << endl; 

Gibt mir eine Zugriffsverletzung. Ich habe das gleiche in anderen Cuda-Programmen gemacht, wo dies kein Problem war. Hat es etwas damit zu tun, wie die komplexe Variable funktioniert, oder handelt es sich um menschliches Versagen?

Wenn jemand irgendwelche Hinweise hat, was falsch läuft, würde ich es sehr schätzen. Hier ist der Code

#include "cuda_runtime.h" 
#include "device_launch_parameters.h" 
#include <helper_functions.h> 
#include <helper_cuda.h> 

#include <ctime> 
#include <time.h> 
#include <stdio.h> 
#include <iostream> 
#include <math.h> 
#include <cufft.h> 
#include <fstream> 

using namespace std; 
typedef float2 Complex; 





__global__ void ComplexMUL(Complex *a, Complex *b) 
{ 
    int i = threadIdx.x; 
    a[i].x = a[i].x * b[i].x - a[i].y*b[i].y; 
    a[i].y = a[i].x * b[i].y + a[i].y*b[i].x; 
} 


int main() 
{ 


    int N = 5; 
    int SIZE = N*N; 


    Complex *fg = new Complex[SIZE]; 
    for (int i = 0; i < SIZE; i++){ 
     fg[i].x = 1; 
     fg[i].y = 0; 
    } 
    Complex *fig = new Complex[SIZE]; 
    for (int i = 0; i < SIZE; i++){ 
     fig[i].x = 1; // 
     fig[i].y = 0; 
    } 
    for (int i = 0; i < 24; i=i+5) 
    { 
     cout << fg[i].x << " " << fg[i + 1].x << " " << fg[i + 2].x << " " << fg[i + 3].x << " " << fg[i + 4].x << endl; 
    } 
    cout << "----------------" << endl; 
    for (int i = 0; i < 24; i = i + 5) 
    { 
     cout << fig[i].x << " " << fig[i + 1].x << " " << fig[i + 2].x << " " << fig[i + 3].x << " " << fig[i + 4].x << endl; 
    } 
    cout << "----------------" << endl; 

    int mem_size = sizeof(Complex)* SIZE; 


    cufftComplex *d_signal; 
    checkCudaErrors(cudaMalloc((void **) &d_signal, mem_size)); 
    checkCudaErrors(cudaMemcpy(d_signal, fg, mem_size, cudaMemcpyHostToDevice)); 

    cufftComplex *d_filter_kernel; 
    checkCudaErrors(cudaMalloc((void **)&d_filter_kernel, mem_size)); 
    checkCudaErrors(cudaMemcpy(d_filter_kernel, fig, mem_size, cudaMemcpyHostToDevice)); 

    // cout << d_signal[1].x << endl; 
    // CUFFT plan 
    cufftHandle plan; 
    cufftPlan2d(&plan, N, N, CUFFT_C2C); 

    // Transform signal and filter 
    printf("Transforming signal cufftExecR2C\n"); 
    cufftExecC2C(plan, (cufftComplex *)d_signal, (cufftComplex *)d_signal, CUFFT_FORWARD); 
    cufftExecC2C(plan, (cufftComplex *)d_filter_kernel, (cufftComplex *)d_filter_kernel, CUFFT_FORWARD); 

    printf("Launching Complex multiplication<<< >>>\n"); 
    ComplexMUL <<< 32, 256 >> >(d_signal, d_filter_kernel); 

    // Transform signal back 
    printf("Transforming signal back cufftExecC2C\n"); 
    cufftExecC2C(plan, (cufftComplex *)d_signal, (cufftComplex *)d_signal, CUFFT_INVERSE); 

    Complex *result = new Complex[SIZE]; 
    cudaMemcpy(result, d_signal, sizeof(Complex)*SIZE, cudaMemcpyDeviceToHost); 

    for (int i = 0; i < SIZE; i=i+5) 
    { 
     cout << result[i].x << " " << result[i + 1].x << " " << result[i + 2].x << " " << result[i + 3].x << " " << result[i + 4].x << endl; 
    } 

    delete result, fg, fig; 
    cufftDestroy(plan); 
    //cufftDestroy(plan2); 
    cudaFree(d_signal); 
    cudaFree(d_filter_kernel); 

} 

Der obige Code den folgenden Terminal-Ausgabe gibt:

1 1 1 1 1 
1 1 1 1 1 
1 1 1 1 1 
1 1 1 1 1 
1 1 1 1 1 
---------------- 
1 1 1 1 1 
1 1 1 1 1 
1 1 1 1 1 
1 1 1 1 1 
1 1 1 1 1 
---------------- 
Transforming signal cufftExecR2C 
Launching Complex multiplication<<< >>> 
Transforming signal back cufftExecC2C 

625 625 625 625 625 
625 625 625 625 625 
625 625 625 625 625 
625 625 625 625 625 
625 625 625 625 625 
+0

Der Code, den Sie gepostet haben, ist unvollständig und kann nicht kompiliert werden. Könnten Sie das bitte korrigieren? Es ist sehr schwer, Ihnen zu sagen, was falsch sein könnte, ohne den Code zu kompilieren und auszuführen, und ich kann das jetzt nicht tun. – talonmies

+0

Sicher, ich hatte einen unkommentierten Abschnitt, den ich nicht hinzufügen wollte. Ich habe es entfernt und alles jetzt in meinem Beitrag bearbeitet. – LukaK

Antwort

0

Das gibt mir ein 5x5-Array mit den Werten 650: Es liest 625, die 5 * 5 * 5 * 5. Der Faltungsalgorithmus, den Sie verwenden, erfordert eine zusätzliche Division durch N * N. Tatsächlich gibt es in cufft keinen Normierungskoeffizienten in der Vorwärtstransformation. Daher kann Ihre Faltung nicht die einfache Multiplikation der beiden Felder im Frequenzbereich sein. (Manche würden es die Mathematiker DFT nennen und nicht die Ärzte DFT).

Außerdem darf ich nicht aus dem Wert des Signals drucken, nachdem es auf den GPU-Speicher kopiert wurde: Dies ist das Standardverhalten CUDA. Bei der Zuweisung von Speicher auf dem Gerät existieren die Daten im Adressbereich des Gerätespeichers und können von der CPU ohne zusätzlichen Aufwand nicht aufgerufen werden. Suchen Sie nach verwaltet Speicher, oder Zerocopy, um Daten von beiden Seiten des PCI Express zugänglich zu machen (dies wird in vielen anderen Posts diskutiert).

+0

Danke für Ihre Antwort Florenti schätzen es. Das hat mir sehr geholfen! – LukaK

2

Es gibt mehrere Probleme hier:

  1. Sie sind viel zu viele Threads für die Größe der Eingabefelder in der Multiplikation Kernel starten, so dass sollte mit Out-of-bounds Speicherfehlern versagt werden. Ich bin überrascht, dass Sie keinen Laufzeitfehler erhalten.
  2. Ihre erwartete Lösung aus der fft/fft - dot Produkt - ifft Sequenz ist, glaube ich, inkorrekt. Die richtige Lösung wäre eine 5x5-Matrix mit 25 in jedem Eintrag.
  3. Wie deutlich in der Dokumentation cuFFT beschrieben, führt die Bibliothek nicht normalisierte FFTs:

    cuFFT führt un-normalisierte FFTs; Das heißt, die Ausführung einer Vorwärts-FFT an einem Eingabedatensatz gefolgt von einer inversen FFT an der resultierenden Menge ergibt Daten, die gleich der Eingabe sind, skaliert mit der Anzahl der Elemente. Die Skalierung entweder um den Kehrwert der Größe des Datensatzes zu transformieren, bleibt dem Benutzer überlassen, so zu funktionieren, wie er es für richtig hält.

Also von meiner Rechnung, die korrekte Ausgangslösung für Ihren Code sollte eine 5x5-Matrix mit 625 in jedem Eintrag sein, die in jedem Eintrag mit 25 zu einer 5x5-Matrix normalisiert werden würden, das heißt. das erwartete Ergebnis. Ich verstehe nicht, wie das Problem bei (1) nicht zu unterschiedlichen Ergebnissen führt, da der Multiplikationskern ausfallen sollte.

TLDR; Nichts zu sehen hier, bewegen Sie sich ...

+0

Zugriff auf gültige Speicherbereich auf GPU, auch wenn nicht zugewiesen, nicht unbedingt einen Fehler außerhalb der CUDA MEM Check-Tests. Bei diesem kleinen Überlauf fällt der Kernel nicht unbedingt aus. Alle Ihre Punkte bleiben jedoch gültig. –

+0

@FlorentDUGUET: das Eingabearray besteht aus 25 Doppelwörtern. Der Kernelstart verwendet 256 Threads pro Block. Als ich es ausführte (und ja, ich habe es ausgeführt), erzeugte es buchstäblich Hunderte von ungültigen Speicherzugriffsfehlern in cuda-memcheck. – talonmies

+0

Danke für Ihre Antwort talonmies ich schätze es. Das hat mir sehr geholfen! – LukaK