Forum Discussion
The weird aggressive aocl optimization "removing unnecessary storage to local memory"
- 7 years ago
With respect to functional verification, what I do is that I construct my host code in a way that both run-time and offline compilation are supported, the latter for FPGAs and the former for other devices, and I use AMD's OpenCL SDK for other devices. In this case, as long as the run-time OpenCL driver is installed, the same host code can then be used to execute the same kernel on any type of CPU, GPU or FPGA. You can take a look at the host code/makefiles of the optimized benchmarks in the following repository as example of achieving this:
https://github.com/fpga-opencl-benchmarks/rodinia_fpga
I emulated all of those kernels on CPUs/GPUs using the same host and kernel codes. What I would tell you is that if an NDRange kernel with sufficiently large local and global size performs correctly on a GPU, it should also perform correctly on an FPGA (unless there is a bug in the FPGA compiler). A CPU should also work fine even if the whole kernel runs on one core, since there will still be multiple threads (work-items) running on that core that could be issued out of order and this is usually enough to show concurrency issues but a GPU would likely be more trustworthy in this case.
With respect to, let's say HDL vs. OpenCL, many old-school HDL programmers tend to think that OpenCL or HLS tools in general are insufficient and it is possible to achieve better results using HDL. This is indeed true in some cases like latency-sensitive or low-power applications where clock-by-clock control over the code is required, or applications that are limited by logic resources, but I would not say this is the case for high-throughput applications where limitation is Memory/PCI-E bandwidth or DSP count since these limitations are independent of the programming language. With respect to the particular case of unpipelinable nested loops, HDL or OpenCL would not make a difference. If you have a regular outer loop with an irregular inner loop, the outer loop cannot be pipelined; it doesn't matter how you "describe" the code. There are two ways to approach such loops on FPGAs:
1- Use NDRange and let the run-time work-item scheduler do its best in maximizing pipeline efficiency and minimizing the average loop II.
2- Collapse the nested loop as long as it is not too irregular and get an II of one at the cost of a noticeable Fmax hit. Though by "collapse" I mean manual collapse and not the compiler's "coalesce" pragma. Take a look at Section 3.2.4.3 in this document:
https://arxiv.org/abs/1810.09773
Even though the provided example involves collapsing a regular nested loop, this optimization also sometimes applies to irregular nested loops. I such case, the condition inside the collapsed loop that is used to increment the variable of the original outer loop will have more than one statement (which complicates the critical path and reduces the Fmax). Indeed the possibility also exists to implement parts of your application in HDL and use it as an HDL library in an OpenCL kernel but you are going to run into complications if your HDL library does not have a fixed latency and I highly doubt you would be able to achieve much better results in the end.
Finally, with respect to NDRange vs. Single Work-item, I recommend reading Section 3.1 (and particularly 3.1.4) of the document I posted above.
Why dont you attached the *.cl files here so that we can start comment about it?
Hi KTan9,
I really appreciate your help and time!
I just attached all (.h and .cl) files (as a .zip file, Hiratz_device.zip) that are needed by the compilation. The main .cl file is decom_comp.cl that includes all other eight .h files.
I'm implementing the zfp floating point compression algorithm (https://computation.llnl.gov/projects/floating-point-compression) (version 0.5.4). The kernel "decomp" is for decompression, and the kernel "compress" is responsible for compression.
Naturally, zfp's block-level decompression/compression is very suitable for GPU's SIMD processing. For example, given a N x N matrix, it can be split into N_SEG segments or sections that are then processed by multiple work items at the same time. However, I'm still hoping that I can achieve the same acceleration effect on Intel FPGA with all possible optimizations.
Unfortunately, there are still some problems with my current version I attached here. For its NDRange version, besides the warnings I mentioned, the num_simd_work_items(n) also cannot work if I put them before the kernel function. If I increases the number of work items and the matrix dimension size (i.e., N), the kernel's running is not stable any more, which sometimes shows wrong results (the emulation always show the correct results). For its task (single work item) version, the current version cannot give correct results. If I replace "zfp[MAX_SEG]" and "stream [MAX_SEG]" with global pointers in the kernel arguments (using global memory), the kernel can work well but it is quite slow.
Two local memory arrays, "zfp[MAX_SEG]" and "stream [MAX_SEG]", are the culprits that cause the warnings I mentioned before. But according to the program semantics, all related code lines that cause these warnings should not be removed (otherwise the emulation running will give wrong results).
The most complex two nested loops are in "decode_ints_uint64(...) " in decode.2h and in "encode_ints_uint64" in encode_2d.h, respectively. Both look similar and each one is 3-level nested loop and the inner 2-level nested loop is so irregular that I cannot find any effective methods to rewrite or optimize it.
In addition, I also tried to use channel to transfer the lseg_size[] from the kernel "compress" to the kernel "merge_stream" but failed. The reason is related to the kernel "compress".
Overall, it seems that most mentioned optimizations in the manual of "Best Practices Guide" are hard to be applied to this application ...
The compilation commands lines I am using are as follows:
1) for quick resource utilization report
aoc -v -c -I./device device/decom_comp.cl -o decom_comp.aocx -g board=pac_a10
2) complete compilation
aoc -v -I./device device/decom_comp.cl -o decom_comp.aocx -g board=pac_a10
Either 1) or 2) will show the warnings I mentioned before if you run them on Intel vLab (Harp) machine. But eventually it will be compiled successfully.
I've been stuck in the above-mentioned problems for over half a month. I appreciate your help so much!