Implementação da Transformada de Fourier Real Unidimensional (aclRfft1D) com CANN

Visão Geral do Operador aclRfft1D

O operador aclRfft1D realiza a Transformada de Fourier Rápida para sinais reais (RFFT) em um tensor de entrada. Diferente da FFT padrão, a RFFT aproveita a simetria hermitiana de sinais reais para fornecer apenas as frequências positivas, resultando em uma computação mais eficiente.

Suporte de Hardware

Plataforma Suporte
Ascend 950PR / Ascend 950DT Não
Produtos de Treinamento/Inferência Atlas A3 Sim
Produtos de Treinamento/Inferência Atlas A2 Sim
Produtos de Inferência Atlas 200I/500 A2 Não
Série de Inferência Atlas (Geral) Não
Série de Treinamento Atlas (Geral) Não

Definição Matemática

A transformação segue a fórmula clássica da DFT aplicada a uma sequência de entrada real:

$$ X_{k} = \sum_{n=0}^{N-1} x_{n} \cdot e^{-i2\pi \frac{k}{N}n} $$

Para uma entrada real $x$, a saída $X_k$ contém apenas os componentes de frequência não negativos, economizando espaço de memória.

Protótipos das Funções

A execução do operador é dividida em duas etapas: cálculo do espaço de trabalho (workspace) e a execução propriamente dita.

aclnnStatus aclRfft1DGetWorkspaceSize(
  const aclTensor*  input,
  int64_t           signalLength,
  int64_t           dimension,
  int64_t           normalization,
  aclTensor*        output,
  uint64_t*         workspaceSize,
  aclOpExecutor**   opExecutor);

aclnnStatus aclRfft1D(
  void*             workspace,
  uint64_t          workspaceSize,
  aclOpExecutor*    opExecutor,
  aclrtStream       rtStream);

Descrição de Parâmetros

aclRfft1DGetWorkspaceSize

Parâmetro Tipo Direção Descrição
input aclTensor* Entrada Tensor de entrada real (FLOAT). Suporta 1 a 7 dimensões.
signalLength int64_t Entrada Comprimento do sinal (n). Se definido, a entrada é truncada ou preenchida com zeros.
dimension int64_t Entrada O eixo sobre o qual a FFT será aplicada.
normalization int64_t Entrada Modo de normalização: 1 (nenhuma), 2 (1/n), 3 (1/sqrt(n)).
output aclTensor* Saída Tensor de saída contendo os resultados complexos.
workspaceSize uint64_t* Saída Tamanho necessário da memória auxiliar no dispositivo.
opExecutor aclOpExecutor** Saída Gerenciador da execução do operador.

Restrições e Comportamento

  • Cálculo Determinístico: O operador aclRfft1D garante resultados determinísticos por padrão.
  • Limites de N: O valor de signalLength deve estar preferencialmente no intervalo [1, 4096] para otimização, com potências de 2 suportadas até 262144.

Exemplo de Integração (C++)

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/acl_rfft1d.h"

#define ASSERT_SUCCESS(ret, msg) \
    if (ret != ACL_SUCCESS) { \
        fprintf(stderr, "[ERROR] %s. Code: %d\n", msg, ret); \
        return ret; \
    }

int main() {
    int32_t devId = 0;
    aclrtStream computeStream;
    
    // Inicialização básica do ambiente Ascend
    ASSERT_SUCCESS(aclInit(nullptr), "Falha no aclInit");
    ASSERT_SUCCESS(aclrtSetDevice(devId), "Falha ao definir dispositivo");
    ASSERT_SUCCESS(aclrtCreateStream(&computeStream), "Erro ao criar stream");

    // Configuração dos tensores
    std::vector<int64_t> shapeIn = {1, 1, 8};
    std::vector<int64_t> shapeOut = {1, 1, 5, 2}; // Saída complexa representada por par de floats
    std::vector<float> rawData = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};

    void *devIn = nullptr, *devOut = nullptr;
    aclTensor *inputTensor = nullptr, *outputTensor = nullptr;

    // Alocação e cópia de dados (Simplificado)
    size_t inBytes = rawData.size() * sizeof(float);
    aclrtMalloc(&devIn, inBytes, ACL_MEM_MALLOC_HUGE_FIRST);
    aclrtMemcpy(devIn, inBytes, rawData.data(), inBytes, ACL_MEMCPY_HOST_TO_DEVICE);

    // Criação dos objetos aclTensor
    int64_t stridesIn[] = {8, 8, 1};
    inputTensor = aclCreateTensor(shapeIn.data(), shapeIn.size(), ACL_FLOAT, stridesIn, 0, ACL_FORMAT_ND, shapeIn.data(), shapeIn.size(), devIn);
    
    size_t outBytes = 10 * sizeof(float);
    aclrtMalloc(&devOut, outBytes, ACL_MEM_MALLOC_HUGE_FIRST);
    int64_t stridesOut[] = {10, 10, 2, 1};
    outputTensor = aclCreateTensor(shapeOut.size() == 4 ? shapeOut.data() : nullptr, shapeOut.size(), ACL_FLOAT, stridesOut, 0, ACL_FORMAT_ND, shapeOut.data(), shapeOut.size(), devOut);

    // Configurações da RFFT
    int64_t n = 8;
    int64_t dim = -1;
    int64_t normMode = 1;
    uint64_t wsSize = 0;
    aclOpExecutor* handle = nullptr;

    // Fase 1: Obter Workspace
    auto status = aclRfft1DGetWorkspaceSize(inputTensor, n, dim, normMode, outputTensor, &wsSize, &handle);
    ASSERT_SUCCESS(status, "Erro ao calcular workspace");

    void* workspaceAddr = nullptr;
    if (wsSize > 0) {
        aclrtMalloc(&workspaceAddr, wsSize, ACL_MEM_MALLOC_HUGE_FIRST);
    }

    // Fase 2: Execução
    status = aclRfft1D(workspaceAddr, wsSize, handle, computeStream);
    ASSERT_SUCCESS(status, "Erro na execução do aclRfft1D");

    aclrtSynchronizeStream(computeStream);

    // Limpeza
    aclDestroyTensor(inputTensor);
    aclDestroyTensor(outputTensor);
    aclrtFree(devIn);
    aclrtFree(devOut);
    if (workspaceAddr) aclrtFree(workspaceAddr);
    aclrtDestroyStream(computeStream);
    aclrtResetDevice(devId);
    aclFinalize();

    return 0;
}

Tags: Ascend CANN RFFT NPU DigitalSignalProcessing

Publicado em 7-13 09:23