• No se han encontrado resultados

The following subsections describes possible suboptimal design choices in our implementation

5.4.1

GPU memory accesses

The subband synthesis filter kernel is implemented using constant memory for filter coefficients, and shared memory for the filter data. This might not be the ideal solution due to the nature of constant memory and the way the kernel is designed to make use of it. Constant memory is cached according to the programming guide [11], and it states that reading from it is as fast as reading from a register, given that all the threads read from the same address. Otherwise it is serialized, assuming that it is still as fast as accessing a register for each access it is faster than global memory. The kernel does a lot of lookups on the filter coefficients, and each thread accesses it own location, which means that it probably is serialized and in worst case results in access to global memory. The shared memory is used to hold the filter data and is accessed as often as constant memory within the inner loops. Access pattern to the shared memory is not as strict as for constant memory and it is also

5.4. DISCUSSING THE RESULTS 59

as fast as accessing a register given that there are no bank conflicts between the threads. If there are bank conflicts the access is serialized. There are 16 banks for devices with compute capability 1.x, and requests are split into one for each half warp (16 threads). Assuming that the shared memory allocated for each thread block is aligned in memory such that the first index starts at the first bank and the consecutive indices are at consecutive banks, the access pattern in the inner loop falls at 16 different banks for a half warp. Starting in the middle of the bank array, at offset 8 (offset 24 modulus 16) for the first lookup in the synthesis kernel, see Listing 4.1 (even subbands), this should avoid bank conflicts if the access does not have to be at an alignment modulus 16 for the first thread in the half warp. For the following iterations of the loop this constraint of accessing 16 different banks for a half warp is fulfilled. The same applies for the three other accesses to shared memory found in Listing 4.1.

5.4.2

Branching in GPU

Within the kernel there are many branches as well, this is to avoid having to store the padded values in global memory. Accessing global memory can take from 400 to 600 clock cycles of memory latency, this is why this choice was made. Branch divergence as described in the programming guide for CUDA states that divergence only occur within a warp, and that threads within a warp are serially executed for each branch path taken, disabling threads not on the path, this is done for each execution path and when complete the threads converge back to the same execution path [11]. Therefore, the condi- tional checking if a given thread block is the first or the last block processing the data should only execute its assigned code block if the conditional is true. Otherwise, the warps not fulfilling the conditional should not execute any of that code. There are some special cases handling the mirroring of the filter data that applies only to some threads, but they do not access global memory so they should not consume to much time. One possibility is also that all the branches are actually executed, which would lead to a great deal of unnecessary data processing and it can be the main reason for the poor performance.

5.4.3

Inverse quantization and alignment

Now, as another comparison basis of the GPU implementation, the inverse quantizer is interpreted. It is simple in its implementation, it only reads quantized data from one buffer, do some simple calculations, and write the result to another buffer. Because it does not write this data to memory

allocated with cudaMalloc3D it is not necessary aligned correct for the sub- sequent lines in a cube. Thus, after the processing the data written to a temporary buffer is copied into memory aware of padding and alignment requirements for data allocated for 3-D access. The time for the whole quan- tization step including memory copy from host till the complete result is placed in the target location is given in Table 5.1. The whole process takes 1.82ms, considering only the quantization including the final copy, it takes 1.42ms. Given that this is the fastest kernel in Table 5.2, the quantization timing-results presented in Table 5.1 might give the best times achievable out of these kernels. If this is the case, the time spent processing including copying from host memory is above that of a pure memory copy from host to device. The time doing the inverse quantization on the device including the copy between different buffers is marginally smaller than that of a pure copy from host to device. In addition, the quantization the way it is im- plemented only gives a compression ratio of 1:4. As this is only one part of the whole compression scheme under investigation, one cannot state that this is an improvement masking the bandwidth limitation between CPU and GPU using compression with subband coding. In addition the the inverse quantized data has to be processed by the subband synthesis filter before it can be used. This would add to the time achieved by the quantizer.

5.5

Proposing improvements to the implemen-