| 84 | // OUTPUT this.loop0, this.loop1, this.loop3 are ready to create the kernel |
| 85 | template<typename T> |
| 86 | threadsMgt<T>::threadsMgt(const T dims[4], const T ndims, |
| 87 | const unsigned nrInputs, const unsigned nrOutputs, |
| 88 | const size_t totalSize, const size_t sizeofT) |
| 89 | : loop0(false) |
| 90 | , loop1(false) |
| 91 | , loop3(false) |
| 92 | , d0(static_cast<unsigned>(dims[0])) |
| 93 | , d1(static_cast<unsigned>(dims[1])) |
| 94 | , d2(static_cast<unsigned>(dims[2])) |
| 95 | , d3(static_cast<unsigned>(dims[3])) |
| 96 | , ndims(ndims) |
| 97 | , totalSize(totalSize) |
| 98 | , dev(opencl::getDevice()) |
| 99 | , maxParallelThreads(getMaxParallelThreads(dev)) |
| 100 | , maxThreads(maxParallelThreads * |
| 101 | (sizeofT * nrInputs * nrInputs > 8 ? 1 : 2)) |
| 102 | , largeVolDivider(1) { |
| 103 | const unsigned cacheLine{getMemoryBusWidth(dev)}; |
| 104 | const size_t L2CacheSize{getL2CacheSize(dev)}; |
| 105 | // The bottleneck of anykernel is dependent on the type of memory |
| 106 | // used. |
| 107 | // a) For very small arrays (elements < maxParallelThreads), each |
| 108 | // element receives it individual thread |
| 109 | // b) For arrays (in+out) smaller |
| 110 | // than 3/2 L2cache, memory access no longer is the bottleneck, |
| 111 | // because enough L2cache is available at any time. Threads are |
| 112 | // limited to reduce scheduling overhead. |
| 113 | // c) For very large arrays and type sizes |
| 114 | // (<long double), 1 thread will not generate enough data to keep |
| 115 | // the memory sync mechanism saturated, so we start loooping inside |
| 116 | // each thread. |
| 117 | // |
| 118 | if (ndims == 1) { |
| 119 | if (d0 > maxThreads) { |
| 120 | loop0 = true; |
| 121 | if (totalSize * 2 > L2CacheSize * 3) { |
| 122 | // General formula to calculate best #loops |
| 123 | // Dedicated GPUs: |
| 124 | // 32/sizeof(T)**2/#outBuffers*(3/4)**(#inBuffers-1) |
| 125 | // Integrated GPUs: |
| 126 | // 4/sizeof(T)/#outBuffers*(3/4)**(#inBuffers-1) |
| 127 | largeVolDivider = cacheLine == 64 ? sizeofT == 1 ? 4 |
| 128 | : sizeofT == 2 ? 2 |
| 129 | : 1 |
| 130 | : (sizeofT == 1 ? 32 |
| 131 | : sizeofT == 2 ? 8 |
| 132 | : 1) / |
| 133 | nrOutputs; |
| 134 | for (unsigned i = 1; i < nrInputs; ++i) |
| 135 | largeVolDivider = largeVolDivider * 3 / 4; |
| 136 | loop0 = largeVolDivider > 1; |
| 137 | } |
| 138 | } |
| 139 | } else { |
| 140 | loop3 = d3 != 1; |
| 141 | if ((d1 > 1) & (d0 * d1 * d2 > maxThreads)) { |
| 142 | loop1 = true; |
| 143 | if ((d0 * sizeofT * 8 > cacheLine * getComputeUnits(dev)) & |
nothing calls this directly
no test coverage detected