This function prepares the pointers towards the temporary memory block, and the NumExprObjects used by threads. Replacement for get_temps_space() and NumExprObject_copy_threadsafe().
| 142 | // the NumExprObjects used by threads. |
| 143 | // Replacement for get_temps_space() and NumExprObject_copy_threadsafe(). |
| 144 | int |
| 145 | prepareThreads( NumExprObject* self, NpyIter *iter, int *pc_error, char **errorMessage ) { |
| 146 | // Variables |
| 147 | int I = 0, R = 0; |
| 148 | npy_intp memOffset = 0, taskFactor, numBlocks; |
| 149 | |
| 150 | // Setup error tracking |
| 151 | gs.ret_code = 0; |
| 152 | gs.pc_error = pc_error; |
| 153 | gs.errorMessage = errorMessage; |
| 154 | |
| 155 | // printf( "prepareThreads #1\n"); |
| 156 | // Stuff from vm_engine_iter_parallel: |
| 157 | NpyIter_GetIterIndexRange(iter, &gs.start, &gs.vlen); |
| 158 | |
| 159 | // Try to make it so each thread gets 16 tasks. This is a compromise |
| 160 | // between 1 task per thread and one block per task. |
| 161 | // RAM: would be nice to benchmark this sort of assumption. |
| 162 | taskFactor = TASKS_PER_THREAD*BLOCK_SIZE1*gs.n_thread; |
| 163 | numBlocks = (gs.vlen - gs.start + taskFactor - 1) / |
| 164 | taskFactor; |
| 165 | gs.task_size = numBlocks * BLOCK_SIZE1; // Note this is much bigger than BLOCK_SIZE1 |
| 166 | |
| 167 | |
| 168 | // Ensure the temporary memory storage is big enough |
| 169 | // printf("Original: gs.tempSize = %d, gs.tempArena = %p\n", gs.tempSize, gs.tempArena ); |
| 170 | if( BLOCK_SIZE1 * gs.n_thread * self->total_temp_itemsize > gs.tempSize ) { |
| 171 | // printf( "temp size too small, resizing #1\n"); |
| 172 | numexpr_set_tempsize( BLOCK_SIZE1 * gs.n_thread * self->total_temp_itemsize ); |
| 173 | // printf("Engorged: gs.tempSize = %d, gs.tempArena = %p\n", gs.tempSize, gs.tempArena ); |
| 174 | } |
| 175 | |
| 176 | // `Do` part of the `for` loop before, use `self` for the first thread. |
| 177 | gs.iter[0] = iter; |
| 178 | gs.params[0] = *self; |
| 179 | gs.params[0].registers = (NumExprReg *)gs.registerArena; |
| 180 | memcpy( gs.params[0].registers, self->registers, self->n_reg*sizeof(NumExprReg) ); |
| 181 | |
| 182 | // Setup temporaries memory pointers for the first NumExprObject |
| 183 | // printf( "self->n_reg = %d, gs.tempArena = %p\n", self->n_reg, gs.tempArena ); |
| 184 | for( R=0; R < self->n_reg; R++ ) { |
| 185 | if( self->registers[R].kind != KIND_TEMP ) continue; |
| 186 | |
| 187 | // printf( "reg#%d has kind %d and itemsize %d\n", R, self->registers[R].kind, (int)self->registers[R].itemsize ); |
| 188 | gs.params[0].registers[R].mem = gs.tempArena + memOffset; |
| 189 | // printf( " thread #0, reg #%d, points to %p\n", R, gs.params[0].registers[R].mem ); |
| 190 | memOffset += BLOCK_SIZE1 * self->registers[R].itemsize; |
| 191 | } |
| 192 | |
| 193 | // Make copies of iterators and NumExprObjects for each additional thread |
| 194 | for( I = 1; I < gs.n_thread; ++I ) { |
| 195 | // TODO: add reduce_iter when you parallelize reductions |
| 196 | gs.iter[I] = NpyIter_Copy(iter); |
| 197 | if (gs.iter[I] == NULL) { |
| 198 | // Error: deallocate all iterators and return an error code. |
| 199 | --I; |
| 200 | // Only the original iterator should ever be called, and not copies. |
| 201 | NpyIter_Deallocate(gs.iter[0]); |
no test coverage detected
searching dependent graphs…