diff --git a/src/Makefile.am b/src/Makefile.am index 3a436f3a4ed2728fa3cab977057474ade60f0a05..6dc507a5bfb846fc19cd6d879e1c7721ca23fda9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,8 +26,8 @@ AM_LDFLAGS = $(LAPACK_LIBS) $(BLAS_LIBS) -version-info 0:0:0 # Build the libgadgetsmp library lib_LTLIBRARIES = libgadgetsmp.la -libgadgetsmp_la_SOURCES = space.c runner.c +libgadgetsmp_la_SOURCES = space.c runner.c queue.c task.c cell.c # List required headers -include_HEADERS = space.h runner.h lock.h +include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h gadgetsmp.h diff --git a/src/cell.c b/src/cell.c new file mode 100644 index 0000000000000000000000000000000000000000..0092644a5a905e268b977ad9dedb4bc701a9f55f --- /dev/null +++ b/src/cell.c @@ -0,0 +1,284 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <float.h> +#include <limits.h> +#include <math.h> + +/* Local headers. */ +#include "cycle.h" +#include "lock.h" +#include "task.h" +#include "part.h" +#include "cell.h" + + +/* Error macro. */ +#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } + + +/* The timers. */ +ticks cell_timer[ cell_timer_count ]; + + +/* Define the timer macros. */ +#ifdef TIMER_VERBOSE + #define TIMER +#endif +#ifdef TIMER + #define TIMER_TIC ticks tic = getticks(); + #define TIMER_TOC(t) timer_toc( t , tic ) + #define TIMER_TIC2 ticks tic2 = getticks(); + #define TIMER_TOC2(t) timer_toc( t , tic2 ) + #ifndef INLINE + # if __GNUC__ && !__GNUC_STDC_INLINE__ + # define INLINE extern inline + # else + # define INLINE inline + # endif + #endif + INLINE ticks timer_toc ( int t , ticks tic ) { + ticks d = (getticks() - tic); + __sync_add_and_fetch( &cell_timer[t] , d ); + return d; + } +#else + #define TIMER_TIC + #define TIMER_TOC(t) +#endif + +/** + * @brief Lock a cell and hold its parents. + * + * @param c The #cell. + */ + +int cell_locktree( struct cell *c ) { + + struct cell *finger, *finger2; + TIMER_TIC + + /* First of all, try to lock this cell. */ + if ( lock_trylock( &c->lock ) != 0 ) { + TIMER_TOC(cell_timer_tree); + return 1; + } + + /* Did somebody hold this cell in the meantime? */ + if ( c->hold ) { + + /* Unlock this cell. */ + if ( lock_unlock( &c->lock ) != 0 ) + error( "Failed to unlock cell." ); + + /* Admit defeat. */ + TIMER_TOC(cell_timer_tree); + return 1; + + } + + /* Climb up the tree and lock/hold/unlock. */ + for ( finger = c->parent ; finger != NULL ; finger = finger->parent ) { + + /* Lock this cell. */ + if ( lock_trylock( &finger->lock ) != 0 ) + break; + + /* Increment the hold. */ + __sync_fetch_and_add( &finger->hold , 1 ); + + /* Unlock the cell. */ + if ( lock_unlock( &finger->lock ) != 0 ) + error( "Failed to unlock cell." ); + + } + + /* If we reached the top of the tree, we're done. */ + if ( finger == NULL ) { + TIMER_TOC(cell_timer_tree); + return 0; + } + + /* Otherwise, we hit a snag. */ + else { + + /* Undo the holds up to finger. */ + for ( finger2 = c->parent ; finger2 != finger ; finger2 = finger2->parent ) + __sync_fetch_and_sub( &finger2->hold , 1 ); + + /* Unlock this cell. */ + if ( lock_unlock( &c->lock ) != 0 ) + error( "Failed to unlock cell." ); + + /* Admit defeat. */ + TIMER_TOC(cell_timer_tree); + return 1; + + } + + } + + +/** + * @brief Unock a cell's parents. + * + * @param c The #cell. + */ + +void cell_unlocktree( struct cell *c ) { + + struct cell *finger; + TIMER_TIC + + /* First of all, try to unlock this cell. */ + if ( lock_unlock( &c->lock ) != 0 ) + error( "Failed to unlock cell." ); + + /* Climb up the tree and unhold the parents. */ + for ( finger = c->parent ; finger != NULL ; finger = finger->parent ) + __sync_fetch_and_sub( &finger->hold , 1 ); + + TIMER_TOC(cell_timer_tree); + + } + + +/** + * @brief Sort the parts into eight bins along the given pivots. + * + * @param c The #cell array to be sorted. + */ + +void cell_split ( struct cell *c ) { + + int i, j, k, kk; + struct part temp, *parts = c->parts; + int left[8], right[8]; + double pivot[3]; + + /* Init the pivot. */ + for ( k = 0 ; k < 3 ; k++ ) + pivot[k] = c->loc[k] + c->h[k]/2; + + /* Split along the x-axis. */ + i = 0; j = c->count - 1; + while ( i <= j ) { + while ( i <= c->count-1 && parts[i].x[0] <= pivot[0] ) + i += 1; + while ( j >= 0 && parts[j].x[0] > pivot[0] ) + j -= 1; + if ( i < j ) { + temp = parts[i]; parts[i] = parts[j]; parts[j] = temp; + } + } + for ( k = 0 ; k <= j ; k++ ) + if ( parts[k].x[0] > pivot[0] ) + error( "cell_split: sorting failed." ); + for ( k = i ; k < c->count ; k++ ) + if ( parts[k].x[0] < pivot[0] ) + error( "cell_split: sorting failed." ); + left[1] = i; right[1] = c->count - 1; + left[0] = 0; right[0] = j; + + /* Split along the y axis, twice. */ + for ( k = 1 ; k >= 0 ; k-- ) { + i = left[k]; j = right[k]; + while ( i <= j ) { + while ( i <= right[k] && parts[i].x[1] <= pivot[1] ) + i += 1; + while ( j >= left[k] && parts[j].x[1] > pivot[1] ) + j -= 1; + if ( i < j ) { + temp = parts[i]; parts[i] = parts[j]; parts[j] = temp; + } + } + for ( kk = left[k] ; kk <= j ; kk++ ) + if ( parts[kk].x[1] > pivot[1] ) { + printf( "cell_split: ival=[%i,%i], i=%i, j=%i.\n" , left[k] , right[k] , i , j ); + error( "sorting failed (left)." ); + } + for ( kk = i ; kk <= right[k] ; kk++ ) + if ( parts[kk].x[1] < pivot[1] ) + error( "sorting failed (right)." ); + left[2*k+1] = i; right[2*k+1] = right[k]; + left[2*k] = left[k]; right[2*k] = j; + } + + /* Split along the z axis, four times. */ + for ( k = 3 ; k >= 0 ; k-- ) { + i = left[k]; j = right[k]; + while ( i <= j ) { + while ( i <= right[k] && parts[i].x[2] <= pivot[2] ) + i += 1; + while ( j >= left[k] && parts[j].x[2] > pivot[2] ) + j -= 1; + if ( i < j ) { + temp = parts[i]; parts[i] = parts[j]; parts[j] = temp; + } + } + for ( kk = left[k] ; kk <= j ; kk++ ) + if ( parts[kk].x[2] > pivot[2] ) { + printf( "cell_split: ival=[%i,%i], i=%i, j=%i.\n" , left[k] , right[k] , i , j ); + error( "sorting failed (left)." ); + } + for ( kk = i ; kk <= right[k] ; kk++ ) + if ( parts[kk].x[2] < pivot[2] ) { + printf( "cell_split: ival=[%i,%i], i=%i, j=%i.\n" , left[k] , right[k] , i , j ); + error( "sorting failed (right)." ); + } + left[2*k+1] = i; right[2*k+1] = right[k]; + left[2*k] = left[k]; right[2*k] = j; + } + + /* Store the counts and offsets. */ + for ( k = 0 ; k < 8 ; k++ ) { + c->progeny[k]->count = right[k] - left[k] + 1; + if ( c->progeny[k]->count < 0 ) + abort(); + c->progeny[k]->parts = &c->parts[ left[k] ]; + } + + /* Verify a few sub-cells. */ + /* for ( k = 0 ; k < c->progeny[0]->count ; k++ ) + if ( c->progeny[0]->parts[k].x[0] > pivot[0] || + c->progeny[0]->parts[k].x[1] > pivot[1] || + c->progeny[0]->parts[k].x[2] > pivot[2] ) + error( "Sorting failed (progeny=0)." ); + for ( k = 0 ; k < c->progeny[1]->count ; k++ ) + if ( c->progeny[1]->parts[k].x[0] > pivot[0] || + c->progeny[1]->parts[k].x[1] > pivot[1] || + c->progeny[1]->parts[k].x[2] <= pivot[2] ) + error( "Sorting failed (progeny=1)." ); + for ( k = 0 ; k < c->progeny[2]->count ; k++ ) + if ( c->progeny[2]->parts[k].x[0] > pivot[0] || + c->progeny[2]->parts[k].x[1] <= pivot[1] || + c->progeny[2]->parts[k].x[2] > pivot[2] ) + error( "Sorting failed (progeny=2)." ); */ + + } + + diff --git a/src/cell.h b/src/cell.h new file mode 100644 index 0000000000000000000000000000000000000000..0577d3c71c678ce56ca013915d3df18c87e50e2d --- /dev/null +++ b/src/cell.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + + + +/* The queue timers. */ +enum { + cell_timer_none = 0, + cell_timer_tree, + cell_timer_count, + }; +extern ticks cell_timer[ cell_timer_count ]; + + +/* Structure to store the data of a single cell. */ +struct cell { + + /* The cell location on the grid. */ + double loc[3]; + + /* The cell dimensions. */ + double h[3]; + + /* Max radii in this cell. */ + double r_max; + + /* The depth of this cell in the tree. */ + int depth, split; + + /* Nr of parts. */ + int count; + + /* Pointers to the particle data. */ + struct part *parts; + + /* Pointers for the sorted indices. */ + struct entry *sort; + + /* Number of pairs associated with this cell. */ + int nr_pairs; + + /* Pointers to the next level of cells. */ + struct cell *progeny[8]; + + /* Parent cell. */ + struct cell *parent; + + /* The tasks computing this cell's sorts. */ + struct task *sorts[14]; + + /* Number of tasks this cell is waiting for and whether it is in use. */ + int wait; + + /* Is the data of this cell being used in a sub-cell? */ + int hold; + + /* Spin lock for various uses. */ + lock_type lock; + + /* Linking pointer for "memory management". */ + struct cell *next; + + } __attribute__((aligned (64))); + + +/* Function prototypes. */ +void cell_split ( struct cell *c ); +int cell_locktree( struct cell *c ); +void cell_unlocktree( struct cell *c ); diff --git a/src/part.h b/src/part.h new file mode 100644 index 0000000000000000000000000000000000000000..8a95bf05992b8837dda93da5910e568360bc4499 --- /dev/null +++ b/src/part.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + + +/* Some constants. */ +#define part_maxwait 3 +#define part_maxunlock 39 + + +/* Data of a single particle. */ +struct part { + + /* Particle position. */ + double x[3]; + + /* Particle cutoff radius. */ + float r; + + /* Particle time-step. */ + float dt; + + /* Particle ID. */ + int id; + + /* Number of pairwise interactions. */ + double count, count_dh; + int icount; + + } __attribute__((aligned (32))); + + diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000000000000000000000000000000000000..4b6744c8b8083276001c2cea6ab636184649ef9c --- /dev/null +++ b/src/queue.c @@ -0,0 +1,352 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <math.h> +#include <float.h> +#include <limits.h> +#include <omp.h> +#include <sched.h> + +/* Local headers. */ +#include "cycle.h" +#include "lock.h" +#include "task.h" +#include "cell.h" +#include "queue.h" + +/* Error macro. */ +#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } + +/* Define the timer macros. */ +#ifdef TIMER_VERBOSE + #define TIMER +#endif +#ifdef TIMER + #define TIMER_TIC ticks tic = getticks(); + #define TIMER_TOC(t) timer_toc( t , tic ) + #define TIMER_TIC2 ticks tic2 = getticks(); + #define TIMER_TOC2(t) timer_toc( t , tic2 ) + #ifndef INLINE + # if __GNUC__ && !__GNUC_STDC_INLINE__ + # define INLINE extern inline + # else + # define INLINE inline + # endif + #endif + INLINE ticks timer_toc ( int t , ticks tic ) { + ticks d = (getticks() - tic); + __sync_add_and_fetch( &queue_timer[t] , d ); + return d; + } +#else + #define TIMER_TIC + #define TIMER_TOC(t) +#endif + + +/* Counter macros. */ +#ifdef COUNTER + #define COUNT(c) ( __sync_add_and_fetch( &queue_counter[ c ] , 1 ) ) +#else + #define COUNT(c) +#endif + + +/* The timers. */ +ticks queue_timer[ queue_timer_count ]; + +/* The counters. */ +int queue_counter[ queue_counter_count ]; + + + +/** + * @brief Initialize the given queue. + * + * @param q The #queue. + * @param size The maximum size of the queue. + * @param tasks List of tasks to which the queue indices refer to. + */ + +void queue_init ( struct queue *q , int size , struct task *tasks ) { + + /* Allocate the task list. */ + q->size = size; + if ( ( q->tid = (int *)malloc( sizeof(int) * size ) ) == NULL ) + error( "Failed to allocate queue tids." ); + q->tasks = tasks; + + /* Init counters. */ + q->count = 0; + q->next = 0; + + /* Init the queue lock. */ + if ( lock_init( &q->lock ) != 0 ) + error( "Failed to init queue lock." ); + + } + + +/** + * @brief Get a task free of dependencies and conflicts. + * + * @param q The task #queue. + * @param blocking Block until access to the queue is granted. + * @param keep Remove the returned task from this queue. + */ + +struct task *queue_gettask ( struct queue *q , int blocking , int keep ) { + + int k, tid = -1, qcount, *qtid = q->tid; + lock_type *qlock = &q->lock; + struct task *qtasks = q->tasks, *res = NULL; + TIMER_TIC + + /* If there are no tasks, leave immediately. */ + if ( q->next >= q->count ) { + TIMER_TOC(queue_timer_gettask); + return NULL; + } + + /* Main loop, while there are tasks... */ + while ( q->next < q->count ) { + + /* Grab the task lock. */ + // if ( blocking ) { + if ( lock_lock( qlock ) != 0 ) + error( "Locking the task_lock failed.\n" ); + // } + // else { + // if ( lock_trylock( qlock ) != 0 ) + // break; + // } + + /* Loop over the remaining task IDs. */ + qcount = q->count; + for ( k = q->next ; k < qcount ; k++ ) { + + /* Put a finger on the task. */ + res = &qtasks[ qtid[k] ]; + + /* Is this task blocked? */ + if ( res->wait ) + continue; + + /* Different criteria for different types. */ + if ( res->type == tid_self || (res->type == tid_sub && res->cj == NULL) ) { + if ( res->ci->hold || cell_locktree( res->ci ) != 0 ) + continue; + } + else if ( res->type == tid_pair || (res->type == tid_sub && res->cj != NULL) ) { + if ( res->ci->hold || res->cj->hold || res->ci->wait || res->cj->wait ) + continue; + if ( cell_locktree( res->ci ) != 0 ) + continue; + if ( cell_locktree( res->cj ) != 0 ) { + cell_unlocktree( res->ci ); + continue; + } + } + + /* If we made it this far, we're safe. */ + break; + + } /* loop over the task IDs. */ + + /* Did we get a task? */ + if ( k < qcount ) { + + /* Do we need to swap? */ + if ( k != q->next ) + COUNT(queue_counter_swap); + + /* get the task ID. */ + tid = qtid[k]; + + /* Remove the task? */ + if ( keep ) { + + /* Bubble-up. */ + q->count = qcount - 1; + for ( ; k < qcount - 1 ; k++ ) + qtid[k] = qtid[k+1]; + + } + + /* No, leave it in the queue. */ + else { + + TIMER_TIC2 + + /* Bubble-down the task. */ + while ( k > q->next ) { + qtid[ k ] = qtid[ k-1 ]; + k -= 1; + } + qtid[ q->next ] = tid; + + /* up the counter. */ + q->next += 1; + + TIMER_TOC2(queue_timer_bubble); + + } + + } + + /* Release the task lock. */ + if ( lock_unlock( qlock ) != 0 ) + error( "Unlocking the task_lock failed.\n" ); + + /* Leave? */ + if ( tid >= 0 ) { + TIMER_TOC(queue_timer_gettask); + return &qtasks[tid]; + } + else if ( !blocking ) + break; + + } /* while there are tasks. */ + + /* No beef. */ + TIMER_TOC(queue_timer_gettask); + return NULL; + + } + + +/** + * @brief Sort the tasks IDs according to their weight and constraints. + * + * @param q The #queue. + */ + +void queue_sort ( struct queue *q ) { + + struct { + short int lo, hi; + } qstack[20]; + int qpos, i, j, k, lo, hi, imin, temp; + int pivot_weight, pivot_wait; + int *weight, *wait; + int *data = q->tid; + struct task *t; + + /* Allocate and pre-compute each task's weight. */ + if ( ( weight = (int *)alloca( sizeof(int) * q->count ) ) == NULL || + ( wait = (int *)alloca( sizeof(int) * q->count ) ) == NULL ) + error( "Failed to allocate weight buffer." ); + for ( k = 0 ; k < q->count ; k++ ) { + t = &q->tasks[ q->tid[k] ]; + switch ( t->type ) { + case tid_self: + wait[k] = t->rank; + weight[k] = 0; // t->ci->count * t->ci->count; + break; + case tid_pair: + wait[k] = t->rank; + weight[k] = 0; // t->ci->count * t->cj->count; + break; + case tid_sub: + wait[k] = t->rank; + weight[k] = 0; // (t->cj == NULL) ? t->ci->count * t->ci->count : t->ci->count * t->cj->count; + break; + case tid_sort: + wait[k] = t->rank; + weight[k] = 0; // t->ci->count; + break; + } + } + + /* Sort tasks. */ + qstack[0].lo = 0; qstack[0].hi = q->count - 1; qpos = 0; + while ( qpos >= 0 ) { + lo = qstack[qpos].lo; hi = qstack[qpos].hi; + qpos -= 1; + if ( hi - lo < 15 ) { + for ( i = lo ; i < hi ; i++ ) { + imin = i; + for ( j = i+1 ; j <= hi ; j++ ) + if ( ( wait[ j ] < wait[ imin ] ) || + ( wait[ j ] == wait[ imin ] && weight[ j ] > weight[ imin ] ) ) + if ( imin != i ) { + temp = data[imin]; data[imin] = data[i]; data[i] = temp; + temp = wait[imin]; wait[imin] = wait[i]; wait[i] = temp; + temp = weight[imin]; weight[imin] = weight[i]; weight[i] = temp; + } + } + } + else { + pivot_weight = weight[ ( lo + hi ) / 2 ]; + pivot_wait = wait[ ( lo + hi ) / 2 ]; + i = lo; j = hi; + while ( i <= j ) { + while ( ( wait[ i ] < pivot_wait ) || + ( wait[ i ] == pivot_wait && weight[ i ] > pivot_weight ) ) + i++; + while ( ( wait[ j ] > pivot_wait ) || + ( wait[ j ] == pivot_wait && weight[ j ] < pivot_weight ) ) + j--; + if ( i <= j ) { + if ( i < j ) { + temp = data[i]; data[i] = data[j]; data[j] = temp; + temp = wait[i]; wait[i] = wait[j]; wait[j] = temp; + temp = weight[i]; weight[i] = weight[j]; weight[j] = temp; + } + i += 1; j -= 1; + } + } + if ( j > ( lo + hi ) / 2 ) { + if ( lo < j ) { + qpos += 1; + qstack[qpos].lo = lo; + qstack[qpos].hi = j; + } + if ( i < hi ) { + qpos += 1; + qstack[qpos].lo = i; + qstack[qpos].hi = hi; + } + } + else { + if ( i < hi ) { + qpos += 1; + qstack[qpos].lo = i; + qstack[qpos].hi = hi; + } + if ( lo < j ) { + qpos += 1; + qstack[qpos].lo = lo; + qstack[qpos].hi = j; + } + } + } + } + + } + + diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000000000000000000000000000000000000..4f4b2670ada03cdc3fc97c3b645a744f26e56f2a --- /dev/null +++ b/src/queue.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + + +/* The queue timers themselves. */ +enum { + queue_timer_none = 0, + queue_timer_gettask, + queue_timer_bubble, + queue_timer_count, + }; +extern ticks queue_timer[ queue_timer_count ]; + + +/* Counters. */ +enum { + queue_counter_swap = 0, + queue_counter_count, + }; +extern int queue_counter[ queue_counter_count ]; + + +/** The queue struct. */ +struct queue { + + /* The lock to access this queue. */ + lock_type lock; + + /* Size, count and next element. */ + int size, count, next; + + /* The actual tasks to which the indices refer. */ + struct task *tasks; + + /* The task indices. */ + int *tid; + + } __attribute__((aligned (64))); + + +/* Function prototypes. */ +struct task *queue_gettask ( struct queue *q , int blocking , int keep ); +void queue_init ( struct queue *q , int size , struct task *tasks ); +void queue_sort ( struct queue *q ); diff --git a/src/runner.c b/src/runner.c index 53caf5f41d5e08780e28ea966ae25dbd785df754..644cbed88444d4963cb52207377911d6fb827ddf 100644 --- a/src/runner.c +++ b/src/runner.c @@ -34,7 +34,11 @@ /* Local headers. */ #include "cycle.h" #include "lock.h" +#include "task.h" +#include "part.h" +#include "cell.h" #include "space.h" +#include "queue.h" #include "runner.h" /* Error macro. */ @@ -70,117 +74,6 @@ const char runner_flip[27] = { 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }; -/** - * @brief Sort the tasks IDs according to their weight and constraints. - * - * @param q The #queue. - */ - -void queue_sort ( struct queue *q ) { - - struct { - short int lo, hi; - } qstack[20]; - int qpos, i, j, k, lo, hi, imin, temp; - int pivot_weight, pivot_wait; - int *weight, *wait; - int *data = q->tid; - struct task *t; - - /* Allocate and pre-compute each task's weight. */ - if ( ( weight = (int *)alloca( sizeof(int) * q->count ) ) == NULL || - ( wait = (int *)alloca( sizeof(int) * q->count ) ) == NULL ) - error( "Failed to allocate weight buffer." ); - for ( k = 0 ; k < q->count ; k++ ) { - t = &q->tasks[ q->tid[k] ]; - switch ( t->type ) { - case tid_self: - wait[k] = t->rank; - weight[k] = 0; // t->ci->count * t->ci->count; - break; - case tid_pair: - wait[k] = t->rank; - weight[k] = 0; // t->ci->count * t->cj->count; - break; - case tid_sub: - wait[k] = t->rank; - weight[k] = 0; // (t->cj == NULL) ? t->ci->count * t->ci->count : t->ci->count * t->cj->count; - break; - case tid_sort: - wait[k] = t->rank; - weight[k] = 0; // t->ci->count; - break; - } - } - - /* Sort tasks. */ - qstack[0].lo = 0; qstack[0].hi = q->count - 1; qpos = 0; - while ( qpos >= 0 ) { - lo = qstack[qpos].lo; hi = qstack[qpos].hi; - qpos -= 1; - if ( hi - lo < 15 ) { - for ( i = lo ; i < hi ; i++ ) { - imin = i; - for ( j = i+1 ; j <= hi ; j++ ) - if ( ( wait[ j ] < wait[ imin ] ) || - ( wait[ j ] == wait[ imin ] && weight[ j ] > weight[ imin ] ) ) - if ( imin != i ) { - temp = data[imin]; data[imin] = data[i]; data[i] = temp; - temp = wait[imin]; wait[imin] = wait[i]; wait[i] = temp; - temp = weight[imin]; weight[imin] = weight[i]; weight[i] = temp; - } - } - } - else { - pivot_weight = weight[ ( lo + hi ) / 2 ]; - pivot_wait = wait[ ( lo + hi ) / 2 ]; - i = lo; j = hi; - while ( i <= j ) { - while ( ( wait[ i ] < pivot_wait ) || - ( wait[ i ] == pivot_wait && weight[ i ] > pivot_weight ) ) - i++; - while ( ( wait[ j ] > pivot_wait ) || - ( wait[ j ] == pivot_wait && weight[ j ] < pivot_weight ) ) - j--; - if ( i <= j ) { - if ( i < j ) { - temp = data[i]; data[i] = data[j]; data[j] = temp; - temp = wait[i]; wait[i] = wait[j]; wait[j] = temp; - temp = weight[i]; weight[i] = weight[j]; weight[j] = temp; - } - i += 1; j -= 1; - } - } - if ( j > ( lo + hi ) / 2 ) { - if ( lo < j ) { - qpos += 1; - qstack[qpos].lo = lo; - qstack[qpos].hi = j; - } - if ( i < hi ) { - qpos += 1; - qstack[qpos].lo = i; - qstack[qpos].hi = hi; - } - } - else { - if ( i < hi ) { - qpos += 1; - qstack[qpos].lo = i; - qstack[qpos].hi = hi; - } - if ( lo < j ) { - qpos += 1; - qstack[qpos].lo = lo; - qstack[qpos].hi = j; - } - } - } - } - - } - - /** * @brief Sort the tasks in topological order over all queues. * @@ -1083,102 +976,6 @@ void runner_dosort ( struct runner_thread *rt , struct cell *c , int flags ) { } -/** - * @brief Lock a cell and hold its parents. - * - * @param c The #cell. - */ - -int cell_locktree( struct cell *c ) { - - struct cell *finger, *finger2; - TIMER_TIC - - /* First of all, try to lock this cell. */ - if ( lock_trylock( &c->lock ) != 0 ) { - TIMER_TOC(runner_timer_tree); - return 1; - } - - /* Did somebody hold this cell in the meantime? */ - if ( c->hold ) { - - /* Unlock this cell. */ - if ( lock_unlock( &c->lock ) != 0 ) - error( "Failed to unlock cell." ); - - /* Admit defeat. */ - TIMER_TOC(runner_timer_tree); - return 1; - - } - - /* Climb up the tree and lock/hold/unlock. */ - for ( finger = c->parent ; finger != NULL ; finger = finger->parent ) { - - /* Lock this cell. */ - if ( lock_trylock( &finger->lock ) != 0 ) - break; - - /* Increment the hold. */ - __sync_fetch_and_add( &finger->hold , 1 ); - - /* Unlock the cell. */ - if ( lock_unlock( &finger->lock ) != 0 ) - error( "Failed to unlock cell." ); - - } - - /* If we reached the top of the tree, we're done. */ - if ( finger == NULL ) { - TIMER_TOC(runner_timer_tree); - return 0; - } - - /* Otherwise, we hit a snag. */ - else { - - /* Undo the holds up to finger. */ - for ( finger2 = c->parent ; finger2 != finger ; finger2 = finger2->parent ) - __sync_fetch_and_sub( &finger2->hold , 1 ); - - /* Unlock this cell. */ - if ( lock_unlock( &c->lock ) != 0 ) - error( "Failed to unlock cell." ); - - /* Admit defeat. */ - TIMER_TOC(runner_timer_tree); - return 1; - - } - - } - - -/** - * @brief Unock a cell's parents. - * - * @param c The #cell. - */ - -void cell_unlocktree( struct cell *c ) { - - struct cell *finger; - TIMER_TIC - - /* First of all, try to unlock this cell. */ - if ( lock_unlock( &c->lock ) != 0 ) - error( "Failed to unlock cell." ); - - /* Climb up the tree and unhold the parents. */ - for ( finger = c->parent ; finger != NULL ; finger = finger->parent ) - __sync_fetch_and_sub( &finger->hold , 1 ); - - TIMER_TOC(runner_timer_tree); - - } - - /** * @brief Implements a barrier for the #runner threads. * @@ -1446,134 +1243,6 @@ void runner_run ( struct runner *r , int sort_queues ) { } -/** - * @brief Get a task free of dependencies and conflicts. - * - * @param q The task #queue. - * @param blocking Block until access to the queue is granted. - * @param keep Remove the returned task from this queue. - */ - -struct task *queue_gettask ( struct queue *q , int blocking , int keep ) { - - int k, tid = -1, qcount, *qtid = q->tid; - lock_type *qlock = &q->lock; - struct task *qtasks = q->tasks, *res = NULL; - TIMER_TIC - - /* If there are no tasks, leave immediately. */ - if ( q->next >= q->count ) { - TIMER_TOC(runner_timer_queue); - return NULL; - } - - /* Main loop, while there are tasks... */ - while ( q->next < q->count ) { - - /* Grab the task lock. */ - // if ( blocking ) { - if ( lock_lock( qlock ) != 0 ) - error( "Locking the task_lock failed.\n" ); - // } - // else { - // if ( lock_trylock( qlock ) != 0 ) - // break; - // } - - /* Loop over the remaining task IDs. */ - qcount = q->count; - for ( k = q->next ; k < qcount ; k++ ) { - - /* Put a finger on the task. */ - res = &qtasks[ qtid[k] ]; - - /* Is this task blocked? */ - if ( res->wait ) - continue; - - /* Different criteria for different types. */ - if ( res->type == tid_self || (res->type == tid_sub && res->cj == NULL) ) { - if ( res->ci->hold || cell_locktree( res->ci ) != 0 ) - continue; - } - else if ( res->type == tid_pair || (res->type == tid_sub && res->cj != NULL) ) { - if ( res->ci->hold || res->cj->hold || res->ci->wait || res->cj->wait ) - continue; - if ( cell_locktree( res->ci ) != 0 ) - continue; - if ( cell_locktree( res->cj ) != 0 ) { - cell_unlocktree( res->ci ); - continue; - } - } - - /* If we made it this far, we're safe. */ - break; - - } /* loop over the task IDs. */ - - /* Did we get a task? */ - if ( k < qcount ) { - - /* Do we need to swap? */ - if ( k != q->next ) - COUNT(runner_counter_swap); - - /* get the task ID. */ - tid = qtid[k]; - - /* Remove the task? */ - if ( keep ) { - - /* Bubble-up. */ - q->count = qcount - 1; - for ( ; k < qcount - 1 ; k++ ) - qtid[k] = qtid[k+1]; - - } - - /* No, leave it in the queue. */ - else { - - TIMER_TIC2 - - /* Bubble-down the task. */ - while ( k > q->next ) { - qtid[ k ] = qtid[ k-1 ]; - k -= 1; - } - qtid[ q->next ] = tid; - - /* up the counter. */ - q->next += 1; - - TIMER_TOC2(runner_timer_bubble); - - } - - } - - /* Release the task lock. */ - if ( lock_unlock( qlock ) != 0 ) - error( "Unlocking the task_lock failed.\n" ); - - /* Leave? */ - if ( tid >= 0 ) { - TIMER_TOC(runner_timer_queue); - return &qtasks[tid]; - } - else if ( !blocking ) - break; - - } /* while there are tasks. */ - - /* No beef. */ - TIMER_TOC(runner_timer_queue); - return NULL; - - } - - /** * @brief init a runner with the given number of threads, queues, and * the given policy. @@ -1613,17 +1282,8 @@ void runner_init ( struct runner *r , struct space *s , int nr_threads , int nr_ bzero( r->queues , nr_queues * sizeof(struct queue) ); /* Init the queues. */ - for ( k = 0 ; k < nr_queues ; k++ ) { - r->queues[k].size = s->nr_tasks; - if ( ( r->queues[k].tid = (int *)malloc( sizeof(int) * r->queues[k].size ) ) == NULL ) - error( "Failed to allocate queue tids." ); - r->queues[k].count = 0; - r->queues[k].next = 0; - if ( lock_init( &r->queues[k].lock ) != 0 ) - error( "Failed to init queue lock." ); - r->queues[k].tasks = s->tasks; - r->queues[k].r = r; - } + for ( k = 0 ; k < nr_queues ; k++ ) + queue_init( &r->queues[k] , s->nr_tasks , s->tasks ); /* Rank the tasks in topological order. */ runner_ranktasks( r ); diff --git a/src/runner.h b/src/runner.h index 07655fab5cc670212cb4d7286152fe06b3fcf521..1549fe7e16c642e8111311388b0a0d9e0ffae52d 100644 --- a/src/runner.h +++ b/src/runner.h @@ -237,27 +237,6 @@ __attribute__ ((always_inline)) INLINE void iact ( float r2 , float hi , float h -/* A task queue. */ -struct queue { - - /* The lock to access this queue. */ - lock_type lock; - - /* Size, count and next element. */ - int size, count, next; - - /* The runner in which this queue lives. */ - struct runner *r; - - /* The actual tasks to which the indices refer. */ - struct task *tasks; - - /* The task indices. */ - int *tid; - - } __attribute__((aligned (64))); - - /* A struct representing a runner's thread and its data. */ struct runner_thread { @@ -308,4 +287,3 @@ void runner_dopair ( struct runner_thread *rt , struct cell *ci , struct cell *c void runner_doself ( struct runner_thread *rt , struct cell *c ); void runner_dosort ( struct runner_thread *rt , struct cell *c , int flag ); void runner_init ( struct runner *r , struct space *s , int nr_threads , int nr_queues , int policy ); -struct task *queue_gettask ( struct queue *q , int blocking , int keep ); diff --git a/src/space.c b/src/space.c index 90d6f44631c19433c9b8ec7132807a57e68d439c..c8795a0b9961f231fe9a2a96468f3a77d2222a95 100644 --- a/src/space.c +++ b/src/space.c @@ -32,6 +32,9 @@ /* Local headers. */ #include "cycle.h" #include "lock.h" +#include "task.h" +#include "part.h" +#include "cell.h" #include "space.h" #include "runner.h" @@ -44,9 +47,6 @@ /* Split size. */ int space_splitsize = space_splitsize_default; -/* Task type names. */ -const char *taskID_names[tid_count] = { "none" , "sort" , "self" , "pair" , "sub" }; - /* Map shift vector to sortlist. */ const int sortlistID[27] = { /* ( -1 , -1 , -1 ) */ 0 , @@ -288,57 +288,6 @@ struct task *space_addtask ( struct space *s , int type , int flags , int wait , -/** - * @brief Remove an unlock_task from the given task. - * - * @param ta The unlocking #task. - * @param tb The #task that will be unlocked. - */ - -void task_rmunlock( struct task *ta , struct task *tb ) { - - int k; - - for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) - if ( ta->unlock_tasks[k] == tb ) { - ta->nr_unlock_tasks -= 1; - ta->unlock_tasks[k] = ta->unlock_tasks[ ta->nr_unlock_tasks ]; - return; - } - error( "Task not found." ); - - } - - -/** - * @brief Add an unlock_task to the given task. - * - * @param ta The unlocking #task. - * @param tb The #task that will be unlocked. - */ - -void task_addunlock( struct task *ta , struct task *tb ) { - - int k; - - /* Bogus? */ - if ( ta == NULL || tb == NULL ) - return; - - /* Check if ta already unlocks tb. */ - for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) - if ( ta->unlock_tasks[k] == tb ) - return; - - if ( ta->nr_unlock_tasks == task_maxunlock ) - error( "Too many unlock_tasks in task." ); - - ta->unlock_tasks[ ta->nr_unlock_tasks] = tb; - ta->nr_unlock_tasks += 1; - - } - - /** * @brief Split tasks that may be too large. * @@ -1006,120 +955,6 @@ void space_maketasks ( struct space *s , int do_sort ) { } -/** - * @brief Sort the parts into eight bins along the given pivots. - * - * @param c The #cell array to be sorted. - */ - -void cell_split ( struct cell *c ) { - - int i, j, k, kk; - struct part temp, *parts = c->parts; - int left[8], right[8]; - double pivot[3]; - - /* Init the pivot. */ - for ( k = 0 ; k < 3 ; k++ ) - pivot[k] = c->loc[k] + c->h[k]/2; - - /* Split along the x-axis. */ - i = 0; j = c->count - 1; - while ( i <= j ) { - while ( i <= c->count-1 && parts[i].x[0] <= pivot[0] ) - i += 1; - while ( j >= 0 && parts[j].x[0] > pivot[0] ) - j -= 1; - if ( i < j ) { - temp = parts[i]; parts[i] = parts[j]; parts[j] = temp; - } - } - for ( k = 0 ; k <= j ; k++ ) - if ( parts[k].x[0] > pivot[0] ) - error( "cell_split: sorting failed." ); - for ( k = i ; k < c->count ; k++ ) - if ( parts[k].x[0] < pivot[0] ) - error( "cell_split: sorting failed." ); - left[1] = i; right[1] = c->count - 1; - left[0] = 0; right[0] = j; - - /* Split along the y axis, twice. */ - for ( k = 1 ; k >= 0 ; k-- ) { - i = left[k]; j = right[k]; - while ( i <= j ) { - while ( i <= right[k] && parts[i].x[1] <= pivot[1] ) - i += 1; - while ( j >= left[k] && parts[j].x[1] > pivot[1] ) - j -= 1; - if ( i < j ) { - temp = parts[i]; parts[i] = parts[j]; parts[j] = temp; - } - } - for ( kk = left[k] ; kk <= j ; kk++ ) - if ( parts[kk].x[1] > pivot[1] ) { - printf( "cell_split: ival=[%i,%i], i=%i, j=%i.\n" , left[k] , right[k] , i , j ); - error( "sorting failed (left)." ); - } - for ( kk = i ; kk <= right[k] ; kk++ ) - if ( parts[kk].x[1] < pivot[1] ) - error( "sorting failed (right)." ); - left[2*k+1] = i; right[2*k+1] = right[k]; - left[2*k] = left[k]; right[2*k] = j; - } - - /* Split along the z axis, four times. */ - for ( k = 3 ; k >= 0 ; k-- ) { - i = left[k]; j = right[k]; - while ( i <= j ) { - while ( i <= right[k] && parts[i].x[2] <= pivot[2] ) - i += 1; - while ( j >= left[k] && parts[j].x[2] > pivot[2] ) - j -= 1; - if ( i < j ) { - temp = parts[i]; parts[i] = parts[j]; parts[j] = temp; - } - } - for ( kk = left[k] ; kk <= j ; kk++ ) - if ( parts[kk].x[2] > pivot[2] ) { - printf( "cell_split: ival=[%i,%i], i=%i, j=%i.\n" , left[k] , right[k] , i , j ); - error( "sorting failed (left)." ); - } - for ( kk = i ; kk <= right[k] ; kk++ ) - if ( parts[kk].x[2] < pivot[2] ) { - printf( "cell_split: ival=[%i,%i], i=%i, j=%i.\n" , left[k] , right[k] , i , j ); - error( "sorting failed (right)." ); - } - left[2*k+1] = i; right[2*k+1] = right[k]; - left[2*k] = left[k]; right[2*k] = j; - } - - /* Store the counts and offsets. */ - for ( k = 0 ; k < 8 ; k++ ) { - c->progeny[k]->count = right[k] - left[k] + 1; - if ( c->progeny[k]->count < 0 ) - abort(); - c->progeny[k]->parts = &c->parts[ left[k] ]; - } - - /* Verify a few sub-cells. */ - /* for ( k = 0 ; k < c->progeny[0]->count ; k++ ) - if ( c->progeny[0]->parts[k].x[0] > pivot[0] || - c->progeny[0]->parts[k].x[1] > pivot[1] || - c->progeny[0]->parts[k].x[2] > pivot[2] ) - error( "Sorting failed (progeny=0)." ); - for ( k = 0 ; k < c->progeny[1]->count ; k++ ) - if ( c->progeny[1]->parts[k].x[0] > pivot[0] || - c->progeny[1]->parts[k].x[1] > pivot[1] || - c->progeny[1]->parts[k].x[2] <= pivot[2] ) - error( "Sorting failed (progeny=1)." ); - for ( k = 0 ; k < c->progeny[2]->count ; k++ ) - if ( c->progeny[2]->parts[k].x[0] > pivot[0] || - c->progeny[2]->parts[k].x[1] <= pivot[1] || - c->progeny[2]->parts[k].x[2] > pivot[2] ) - error( "Sorting failed (progeny=2)." ); */ - - } - /** * @brief Split cells that contain too many particles. diff --git a/src/space.h b/src/space.h index ebe228d28e30780d1b041efaa4a1a1711827889c..61bf2e5503f52f0a21fc23eb5b533e897a224c0e 100644 --- a/src/space.h +++ b/src/space.h @@ -26,8 +26,6 @@ #define space_splitratio 0.875 #define space_splitsize_default 800 #define space_dosub 1 -#define task_maxwait 3 -#define task_maxunlock 39 /* Split size. */ @@ -37,32 +35,6 @@ extern int space_splitsize; extern const int sortlistID[27]; -/* The different task IDs. */ -enum taskIDs { - tid_none = 0, - tid_sort, - tid_self, - tid_pair, - tid_sub, - tid_count - }; -extern const char *taskID_names[]; - - -/* Data of a task. */ -struct task { - - int type, flags, wait, rank, done; - - int nr_unlock_tasks; - struct task *unlock_tasks[ task_maxunlock ]; - - int nr_unlock_cells; - struct cell *ci, *cj, *unlock_cells[2]; - - } __attribute__((aligned (64))); - - /* Entry in a list of sorted indices. */ struct entry { float d; @@ -70,79 +42,6 @@ struct entry { }; -/* Data of a single particle. */ -struct part { - - /* Particle position. */ - double x[3]; - - /* Particle cutoff radius. */ - float r; - - /* Particle time-step. */ - float dt; - - /* Particle ID. */ - int id; - - /* Number of pairwise interactions. */ - double count, count_dh; - int icount; - - } __attribute__((aligned (32))); - - -/* Structure to store the data of a single cell. */ -struct cell { - - /* The cell location on the grid. */ - double loc[3]; - - /* The cell dimensions. */ - double h[3]; - - /* Max radii in this cell. */ - double r_max; - - /* The depth of this cell in the tree. */ - int depth, split; - - /* Nr of parts. */ - int count; - - /* Pointers to the particle data. */ - struct part *parts; - - /* Pointers for the sorted indices. */ - struct entry *sort; - - /* Number of pairs associated with this cell. */ - int nr_pairs; - - /* Pointers to the next level of cells. */ - struct cell *progeny[8]; - - /* Parent cell. */ - struct cell *parent; - - /* The tasks computing this cell's sorts. */ - struct task *sorts[14]; - - /* Number of tasks this cell is waiting for and whether it is in use. */ - int wait; - - /* Is the data of this cell being used in a sub-cell? */ - int hold; - - /* Spin lock for various uses. */ - lock_type lock; - - /* Linking pointer for "memory management". */ - struct cell *next; - - } __attribute__((aligned (64))); - - /* The space in which the cells reside. */ struct space { diff --git a/src/task.c b/src/task.c new file mode 100644 index 0000000000000000000000000000000000000000..4e1a7db6f716c2ce0ee9ebd10264adcff56b865c --- /dev/null +++ b/src/task.c @@ -0,0 +1,98 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + +/* Config parameters. */ +#include "../config.h" + +/* Some standard headers. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <math.h> +#include <float.h> +#include <limits.h> +#include <omp.h> +#include <sched.h> + +/* Local headers. */ +#include "cycle.h" +#include "lock.h" +#include "task.h" + + +/* Task type names. */ +const char *taskID_names[tid_count] = { "none" , "sort" , "self" , "pair" , "sub" }; + + +/* Error macro. */ +#define error(s) { printf( "%s:%s:%i: %s\n" , __FILE__ , __FUNCTION__ , __LINE__ , s ); abort(); } + + +/** + * @brief Remove an unlock_task from the given task. + * + * @param ta The unlocking #task. + * @param tb The #task that will be unlocked. + */ + +void task_rmunlock( struct task *ta , struct task *tb ) { + + int k; + + for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) + if ( ta->unlock_tasks[k] == tb ) { + ta->nr_unlock_tasks -= 1; + ta->unlock_tasks[k] = ta->unlock_tasks[ ta->nr_unlock_tasks ]; + return; + } + error( "Task not found." ); + + } + + +/** + * @brief Add an unlock_task to the given task. + * + * @param ta The unlocking #task. + * @param tb The #task that will be unlocked. + */ + +void task_addunlock( struct task *ta , struct task *tb ) { + + int k; + + /* Bogus? */ + if ( ta == NULL || tb == NULL ) + return; + + /* Check if ta already unlocks tb. */ + for ( k = 0 ; k < ta->nr_unlock_tasks ; k++ ) + if ( ta->unlock_tasks[k] == tb ) + return; + + if ( ta->nr_unlock_tasks == task_maxunlock ) + error( "Too many unlock_tasks in task." ); + + ta->unlock_tasks[ ta->nr_unlock_tasks] = tb; + ta->nr_unlock_tasks += 1; + + } + + diff --git a/src/task.h b/src/task.h new file mode 100644 index 0000000000000000000000000000000000000000..7d5d3422b0c91c3eaee2becc04078cbc76dd1d5c --- /dev/null +++ b/src/task.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * This file is part of GadgetSMP. + * Coypright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + ******************************************************************************/ + + +/* Some constants. */ +#define task_maxwait 3 +#define task_maxunlock 39 + + +/* The different task IDs. */ +enum taskIDs { + tid_none = 0, + tid_sort, + tid_self, + tid_pair, + tid_sub, + tid_count + }; + +extern const char *taskID_names[]; + + +/* Data of a task. */ +struct task { + + int type, flags, wait, rank, done; + + int nr_unlock_tasks; + struct task *unlock_tasks[ task_maxunlock ]; + + int nr_unlock_cells; + struct cell *ci, *cj, *unlock_cells[2]; + + } __attribute__((aligned (64))); + + +/* Function prototypes. */ +void task_rmunlock( struct task *ta , struct task *tb ); +void task_addunlock( struct task *ta , struct task *tb );