are you using the matrix more than once
Yes, below is my kernel implementation which is MUSIC DOA estimation.
It will scan the azimuth field of view (-60~60 degree) and elevation field of view (60~95 degree).
At each pair of (azimuth, elevation), it will calculate (a^H)(UU^H)(a).
a (8x1): steering vector of certain pair of (azimuth, elevation)
H: Hermitian operator
U: noise subspace of signal
Ucov (8x8): covariance matrix of noise subspace
I merged UU^H into Ucov, which means the covariance matrix of noise subspace to simplify the multiplication.
So in each pair of (azimuth, elevation), after I calculated 8 steering vectors, I saved them into shared_steering_v.
The covariance matrix of noise subspace (8*8) also saved into shared_mem shared_noise_cov.
And both shared_steering_v and shared_noise_cov will be used more than once.
cfg.n_ant = 8;
__global__ void spectrum_scanning(double *spectrum_out, cuDoubleComplex *noiseSpace, DOAConfig cfg, int azimuth_begin, int azimuth_end, int elevation_begin, int elevation_end) {
extern __shared__ cuDoubleComplex shared_mem[];
cuDoubleComplex *shared_steering_v = shared_mem;
cuDoubleComplex *shared_noise_cov = shared_mem + cfg.n_ant;
double d = cfg.d;
double lambda = cfg.lambda;
int tid = threadIdx.x;
int bid = blockIdx.x * gridDim.y + blockIdx.y;
int row = tid / cfg.n_ant;
int col = tid % cfg.n_ant;
int col_majored_idx = row + col * cfg.n_ant;
int curr_azimuth = azimuth_begin + blockIdx.x;
int curr_elevation = elevation_begin + blockIdx.y;
if (tid < cfg.n_ant) {
double azimuth = curr_azimuth * M_PI / 180.0;
double elevation = curr_elevation * M_PI / 180.0;
double sin_phi = sin(azimuth);
double cos_phi = cos(azimuth);
double sin_theta = sin(elevation);
double cos_theta = cos(elevation);
double phaseShift = 2 * M_PI * d *
(cfg.antenna[tid][0] * (cos_phi * sin_theta) + cfg.antenna[tid][1] * (sin_phi * sin_theta) + cfg.antenna[tid][2] * cos_theta) /
lambda;
shared_steering_v[tid] = make_cuDoubleComplex(cos(phaseShift), sin(phaseShift));
}
if (tid < cfg.n_ant * cfg.n_ant) {
shared_noise_cov[tid] = noiseSpace[tid];
}
__syncthreads();
if (tid < cfg.n_ant * cfg.n_ant) {
shared_noise_cov[col_majored_idx] = cuCmul(shared_noise_cov[col_majored_idx], cuConj(shared_steering_v[col]));
}
__syncthreads();
// reduce axis-Y
int blockSize = cfg.n_ant * cfg.n_ant;
for (int stride = blockSize / 2; stride >= cfg.n_ant; stride >>= 1) {
if (col_majored_idx < stride) {
shared_noise_cov[col_majored_idx] = cuCadd(shared_noise_cov[col_majored_idx], shared_noise_cov[col_majored_idx + stride]);
}
__syncthreads();
}
if (tid < cfg.n_ant) {
shared_steering_v[tid] = cuCmul(shared_noise_cov[tid], shared_steering_v[tid]);
}
__syncthreads();
int steer_size = cfg.n_ant;
for (int stride = steer_size / 2; stride > 0; stride >>= 1) {
if (tid < stride) {
shared_steering_v[tid] = cuCadd(shared_steering_v[tid], shared_steering_v[tid + stride]);
}
__syncthreads();
}
if (tid == 0) {
spectrum_out[bid] = 10 * log10(1.0 / cuCabs(shared_steering_v[0]));
}
}
The way I launch the kernel:
Each time I scan 11 degrees of azimuth FOV, and 36 degree of elevation FOV, so the dim of blockNum_ is (11, 36).
The thread number of each block is 64 for the size of noise subspace covariance matrix is (8*8).
dim3 blockNum_(11, 36);
int threadNum_ = 64;
int sharedMem = m_cfg.n_ant * (m_cfg.n_ant + 1) * sizeof(cuDoubleComplex);
// #pragma unroll
for (int azimuth_row = 0; azimuth_row < (AZIMUTH_END - AZIMUTH_BEGIN); azimuth_row += blockNum_.x) {
int offset = azimuth_row * blockNum_.y;
spectrum_scanning<<<blockNum_, threadNum_, sharedMem, m_streams[azimuth_row % n_stream]>>>(
m_spectrum.data().get() + offset,
m_noise_space_cov.data().get(),
m_cfg,
AZIMUTH_BEGIN + azimuth_row,
AZIMUTH_BEGIN + azimuth_row + blockNum_.x -1,
ELEVATION_BEGIN, ELEVATION_END
);
}
for (int i = 0; i < n_stream; ++i) {
cudaStreamSynchronize(m_streams[i]);
}
Thanks for your kind help.