Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/ready_queue.cfa

    ra2a4566 r078fb05  
    2020
    2121
    22 // #define USE_RELAXED_FIFO
     22#define USE_RELAXED_FIFO
    2323// #define USE_WORK_STEALING
    2424// #define USE_CPU_WORK_STEALING
    25 #define USE_AWARE_STEALING
    2625
    2726#include "bits/defs.hfa"
     
    3029
    3130#include "stdlib.hfa"
    32 #include "limits.hfa"
    3331#include "math.hfa"
    3432
     
    5654#endif
    5755
    58 #if   defined(USE_AWARE_STEALING)
    59         #define READYQ_SHARD_FACTOR 2
    60         #define SEQUENTIAL_SHARD 2
    61 #elif defined(USE_CPU_WORK_STEALING)
     56#if   defined(USE_CPU_WORK_STEALING)
    6257        #define READYQ_SHARD_FACTOR 2
    6358#elif defined(USE_RELAXED_FIFO)
     
    143138        __kernel_rseq_register();
    144139
     140        __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p for RW-Lock\n", proc);
    145141        bool * handle = (bool *)&kernelTLS().sched_lock;
    146142
     
    178174        }
    179175
     176        __cfadbg_print_safe(ready_queue, "Kernel : Registering proc %p done, id %lu\n", proc, n);
     177
    180178        // Return new spot.
    181179        /* paranoid */ verify(n < ready);
     
    192190
    193191        __atomic_store_n(cell, 0p, __ATOMIC_RELEASE);
     192
     193        __cfadbg_print_safe(ready_queue, "Kernel : Unregister proc %p\n", proc);
    194194
    195195        __kernel_rseq_unregister();
     
    244244
    245245//=======================================================================
    246 // caches handling
    247 
    248 struct __attribute__((aligned(128))) __ready_queue_caches_t {
    249         // Count States:
    250         // - 0  : No one is looking after this cache
    251         // - 1  : No one is looking after this cache, BUT it's not empty
    252         // - 2+ : At least one processor is looking after this cache
    253         volatile unsigned count;
    254 };
    255 
    256 void  ?{}(__ready_queue_caches_t & this) { this.count = 0; }
    257 void ^?{}(__ready_queue_caches_t & this) {}
    258 
    259 static inline void depart(__ready_queue_caches_t & cache) {
    260         /* paranoid */ verify( cache.count > 1);
    261         __atomic_fetch_add(&cache.count, -1, __ATOMIC_SEQ_CST);
    262         /* paranoid */ verify( cache.count != 0);
    263         /* paranoid */ verify( cache.count < 65536 ); // This verify assumes no cluster will have more than 65000 kernel threads mapped to a single cache, which could be correct but is super weird.
    264 }
    265 
    266 static inline void arrive(__ready_queue_caches_t & cache) {
    267         // for() {
    268         //      unsigned expected = cache.count;
    269         //      unsigned desired  = 0 == expected ? 2 : expected + 1;
    270         // }
    271 }
    272 
    273 //=======================================================================
    274246// Cforall Ready Queue used for scheduling
    275247//=======================================================================
    276 unsigned long long moving_average(unsigned long long currtsc, unsigned long long instsc, unsigned long long old_avg) {
    277         /* paranoid */ verifyf( currtsc < 45000000000000000, "Suspiciously large current time: %'llu (%llx)\n", currtsc, currtsc );
    278         /* paranoid */ verifyf( instsc  < 45000000000000000, "Suspiciously large insert time: %'llu (%llx)\n", instsc, instsc );
    279         /* paranoid */ verifyf( old_avg < 15000000000000, "Suspiciously large previous average: %'llu (%llx)\n", old_avg, old_avg );
    280 
    281         const unsigned long long new_val = currtsc > instsc ? currtsc - instsc : 0;
    282         const unsigned long long total_weight = 16;
    283         const unsigned long long new_weight   = 4;
    284         const unsigned long long old_weight = total_weight - new_weight;
    285         const unsigned long long ret = ((new_weight * new_val) + (old_weight * old_avg)) / total_weight;
    286         return ret;
     248unsigned long long moving_average(unsigned long long nval, unsigned long long oval) {
     249        const unsigned long long tw = 16;
     250        const unsigned long long nw = 4;
     251        const unsigned long long ow = tw - nw;
     252        return ((nw * nval) + (ow * oval)) / tw;
    287253}
    288254
     
    304270                        lanes.help[idx].tri = 0;
    305271                }
    306 
    307                 caches = alloc( cpu_info.llc_count );
    308                 for( idx; (size_t)cpu_info.llc_count ) {
    309                         (caches[idx]){};
    310                 }
    311272        #else
    312                 lanes.data   = 0p;
    313                 lanes.tscs   = 0p;
    314                 lanes.caches = 0p;
    315                 lanes.help   = 0p;
    316                 lanes.count  = 0;
     273                lanes.data  = 0p;
     274                lanes.tscs  = 0p;
     275                lanes.help  = 0p;
     276                lanes.count = 0;
    317277        #endif
    318278}
     
    325285        free(lanes.data);
    326286        free(lanes.tscs);
    327         free(lanes.caches);
    328287        free(lanes.help);
    329288}
    330289
    331290//-----------------------------------------------------------------------
    332 #if defined(USE_AWARE_STEALING)
    333         __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
    334                 processor * const proc = kernelTLS().this_processor;
    335                 const bool external = (!proc) || (cltr != proc->cltr);
    336                 const bool remote   = hint == UNPARK_REMOTE;
    337 
    338                 unsigned i;
    339                 if( external || remote ) {
    340                         // Figure out where thread was last time and make sure it's valid
    341                         /* paranoid */ verify(thrd->preferred >= 0);
    342                         if(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count) {
    343                                 /* paranoid */ verify(thrd->preferred * READYQ_SHARD_FACTOR < lanes.count);
    344                                 unsigned start = thrd->preferred * READYQ_SHARD_FACTOR;
    345                                 do {
    346                                         unsigned r = __tls_rand();
    347                                         i = start + (r % READYQ_SHARD_FACTOR);
    348                                         /* paranoid */ verify( i < lanes.count );
    349                                         // If we can't lock it retry
    350                                 } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
    351                         } else {
    352                                 do {
    353                                         i = __tls_rand() % lanes.count;
    354                                 } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
    355                         }
    356                 } else {
    357                         do {
    358                                 unsigned r = proc->rdq.its++;
    359                                 i = proc->rdq.id + (r % READYQ_SHARD_FACTOR);
    360                                 /* paranoid */ verify( i < lanes.count );
    361                                 // If we can't lock it retry
    362                         } while( !__atomic_try_acquire( &lanes.data[i].lock ) );
    363                 }
    364 
    365                 // Actually push it
    366                 push(lanes.data[i], thrd);
    367 
    368                 // Unlock and return
    369                 __atomic_unlock( &lanes.data[i].lock );
    370 
    371                 #if !defined(__CFA_NO_STATISTICS__)
    372                         if(unlikely(external || remote)) __atomic_fetch_add(&cltr->stats->ready.push.extrn.success, 1, __ATOMIC_RELAXED);
    373                         else __tls_stats()->ready.push.local.success++;
    374                 #endif
    375         }
    376 
    377         static inline unsigned long long calc_cutoff(const unsigned long long ctsc, const processor * proc, __ready_queue_t & rdq) {
    378                 unsigned start = proc->rdq.id;
    379                 unsigned long long max = 0;
    380                 for(i; READYQ_SHARD_FACTOR) {
    381                         unsigned long long ptsc = ts(rdq.lanes.data[start + i]);
    382                         if(ptsc != -1ull) {
    383                                 /* paranoid */ verify( start + i < rdq.lanes.count );
    384                                 unsigned long long tsc = moving_average(ctsc, ptsc, rdq.lanes.tscs[start + i].ma);
    385                                 if(tsc > max) max = tsc;
    386                         }
    387                 }
    388                 return (max + 2 * max) / 2;
    389         }
    390 
    391         __attribute__((hot)) struct thread$ * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
    392                 /* paranoid */ verify( lanes.count > 0 );
    393                 /* paranoid */ verify( kernelTLS().this_processor );
    394                 /* paranoid */ verify( kernelTLS().this_processor->rdq.id < lanes.count );
    395 
    396                 processor * const proc = kernelTLS().this_processor;
    397                 unsigned this = proc->rdq.id;
    398                 /* paranoid */ verify( this < lanes.count );
    399                 __cfadbg_print_safe(ready_queue, "Kernel : pop from %u\n", this);
    400 
    401                 // Figure out the current cpu and make sure it is valid
    402                 const int cpu = __kernel_getcpu();
    403                 /* paranoid */ verify(cpu >= 0);
    404                 /* paranoid */ verify(cpu < cpu_info.hthrd_count);
    405                 unsigned this_cache = cpu_info.llc_map[cpu].cache;
    406                 __atomic_store_n(&lanes.caches[this / READYQ_SHARD_FACTOR].id, this_cache, __ATOMIC_RELAXED);
    407 
    408                 const unsigned long long ctsc = rdtscl();
    409 
    410                 if(proc->rdq.target == MAX) {
    411                         uint64_t chaos = __tls_rand();
    412                         unsigned ext = chaos & 0xff;
    413                         unsigned other  = (chaos >> 8) % (lanes.count);
    414 
    415                         if(ext < 3 || __atomic_load_n(&lanes.caches[other / READYQ_SHARD_FACTOR].id, __ATOMIC_RELAXED) == this_cache) {
    416                                 proc->rdq.target = other;
    417                         }
    418                 }
    419                 else {
    420                         const unsigned target = proc->rdq.target;
    421                         __cfadbg_print_safe(ready_queue, "Kernel : %u considering helping %u, tcsc %llu\n", this, target, lanes.tscs[target].tv);
    422                         /* paranoid */ verify( lanes.tscs[target].tv != MAX );
    423                         if(target < lanes.count) {
    424                                 const unsigned long long cutoff = calc_cutoff(ctsc, proc, cltr->ready_queue);
    425                                 const unsigned long long age = moving_average(ctsc, lanes.tscs[target].tv, lanes.tscs[target].ma);
    426                                 __cfadbg_print_safe(ready_queue, "Kernel : Help attempt on %u from %u, age %'llu vs cutoff %'llu, %s\n", target, this, age, cutoff, age > cutoff ? "yes" : "no");
    427                                 if(age > cutoff) {
    428                                         thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
    429                                         if(t) return t;
    430                                 }
    431                         }
    432                         proc->rdq.target = MAX;
    433                 }
    434 
    435                 for(READYQ_SHARD_FACTOR) {
    436                         unsigned i = this + (proc->rdq.itr++ % READYQ_SHARD_FACTOR);
    437                         if(thread$ * t = try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.local))) return t;
    438                 }
    439 
    440                 // All lanes where empty return 0p
    441                 return 0p;
    442 
    443         }
    444         __attribute__((hot)) struct thread$ * pop_slow(struct cluster * cltr) with (cltr->ready_queue) {
    445                 unsigned i = __tls_rand() % lanes.count;
    446                 return try_pop(cltr, i __STATS(, __tls_stats()->ready.pop.steal));
    447         }
    448         __attribute__((hot)) struct thread$ * pop_search(struct cluster * cltr) {
    449                 return search(cltr);
    450         }
    451 #endif
    452291#if defined(USE_CPU_WORK_STEALING)
    453292        __attribute__((hot)) void push(struct cluster * cltr, struct thread$ * thrd, unpark_hint hint) with (cltr->ready_queue) {
     
    506345        }
    507346
    508         static inline int pop_getcpu(processor * proc, __ready_queue_caches_t * caches) {
    509                 const int prv = proc->rdq.cpu;
    510                 const int cpu = __kernel_getcpu();
    511                 if( prv != proc->rdq.cpu ) {
    512                         unsigned pidx = cpu_info.llc_map[prv].cache;
    513                         /* paranoid */ verify(pidx < cpu_info.llc_count);
    514 
    515                         unsigned nidx = cpu_info.llc_map[cpu].cache;
    516                         /* paranoid */ verify(pidx < cpu_info.llc_count);
    517 
    518                         depart(caches[pidx]);
    519                         arrive(caches[nidx]);
    520 
    521                         __STATS( /* cpu migs++ */ )
    522                 }
    523                 return proc->rdq.cpu = cpu;
    524         }
    525 
    526347        // Pop from the ready queue from a given cluster
    527348        __attribute__((hot)) thread$ * pop_fast(struct cluster * cltr) with (cltr->ready_queue) {
     
    529350                /* paranoid */ verify( kernelTLS().this_processor );
    530351
    531                 processor * const proc = kernelTLS().this_processor;
    532                 const int cpu = pop_getcpu( proc, caches );
    533                 // const int cpu = __kernel_getcpu();
     352                const int cpu = __kernel_getcpu();
    534353                /* paranoid */ verify(cpu >= 0);
    535354                /* paranoid */ verify(cpu < cpu_info.hthrd_count);
     
    541360                /* paranoid */ verifyf((map.start + map.count) * READYQ_SHARD_FACTOR <= lanes.count, "have %zu lanes but map can go up to %u", lanes.count, (map.start + map.count) * READYQ_SHARD_FACTOR);
    542361
     362                processor * const proc = kernelTLS().this_processor;
    543363                const int start = map.self * READYQ_SHARD_FACTOR;
    544364                const unsigned long long ctsc = rdtscl();
    545365
    546366                // Did we already have a help target
    547                 if(proc->rdq.target == MAX) {
     367                if(proc->rdq.target == -1u) {
    548368                        unsigned long long max = 0;
    549369                        for(i; READYQ_SHARD_FACTOR) {
     
    551371                                if(tsc > max) max = tsc;
    552372                        }
    553                         // proc->rdq.cutoff = (max + 2 * max) / 2;
     373                        proc->rdq.cutoff = (max + 2 * max) / 2;
    554374                        /* paranoid */ verify(lanes.count < 65536); // The following code assumes max 65536 cores.
    555375                        /* paranoid */ verify(map.count < 65536); // The following code assumes max 65536 cores.
     
    564384                        }
    565385
    566                         /* paranoid */ verify(proc->rdq.target != MAX);
     386                        /* paranoid */ verify(proc->rdq.target != -1u);
    567387                }
    568388                else {
     
    575395                        {
    576396                                unsigned target = proc->rdq.target;
    577                                 proc->rdq.target = MAX;
     397                                proc->rdq.target = -1u;
    578398                                lanes.help[target / READYQ_SHARD_FACTOR].tri++;
    579399                                if(moving_average(ctsc - lanes.tscs[target].tv, lanes.tscs[target].ma) > cutoff) {
    580                                         __STATS( __tls_stats()->ready.pop.helped[target]++; )
    581400                                        thread$ * t = try_pop(cltr, target __STATS(, __tls_stats()->ready.pop.help));
    582401                                        proc->rdq.last = target;
    583402                                        if(t) return t;
     403                                        else proc->rdq.target = -1u;
    584404                                }
    585                                 proc->rdq.target = MAX;
     405                                else proc->rdq.target = -1u;
    586406                        }
    587407
    588408                        unsigned last = proc->rdq.last;
    589                         if(last != MAX && moving_average(ctsc - lanes.tscs[last].tv, lanes.tscs[last].ma) > cutoff) {
    590                                 __STATS( __tls_stats()->ready.pop.helped[last]++; )
     409                        if(last != -1u && lanes.tscs[last].tv < cutoff && ts(lanes.data[last]) < cutoff) {
    591410                                thread$ * t = try_pop(cltr, last __STATS(, __tls_stats()->ready.pop.help));
    592411                                if(t) return t;
    593412                        }
    594413                        else {
    595                                 proc->rdq.last = MAX;
     414                                proc->rdq.last = -1u;
    596415                        }
    597416                }
     
    609428                processor * const proc = kernelTLS().this_processor;
    610429                unsigned last = proc->rdq.last;
    611                 if(last != MAX) {
     430                if(last != -1u) {
    612431                        struct thread$ * t = try_pop(cltr, last __STATS(, __tls_stats()->ready.pop.steal));
    613432                        if(t) return t;
    614                         proc->rdq.last = MAX;
     433                        proc->rdq.last = -1u;
    615434                }
    616435
     
    741560                #else
    742561                        unsigned preferred = thrd->preferred;
    743                         const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || preferred == MAX || thrd->curr_cluster != cltr;
     562                        const bool external = (hint != UNPARK_LOCAL) || (!kernelTLS().this_processor) || preferred == -1u || thrd->curr_cluster != cltr;
    744563                        /* paranoid */ verifyf(external || preferred < lanes.count, "Invalid preferred queue %u for %u lanes", preferred, lanes.count );
    745564
     
    793612                processor * proc = kernelTLS().this_processor;
    794613
    795                 if(proc->rdq.target == MAX) {
     614                if(proc->rdq.target == -1u) {
    796615                        unsigned long long min = ts(lanes.data[proc->rdq.id]);
    797616                        for(int i = 0; i < READYQ_SHARD_FACTOR; i++) {
     
    804623                else {
    805624                        unsigned target = proc->rdq.target;
    806                         proc->rdq.target = MAX;
     625                        proc->rdq.target = -1u;
    807626                        const unsigned long long bias = 0; //2_500_000_000;
    808627                        const unsigned long long cutoff = proc->rdq.cutoff > bias ? proc->rdq.cutoff - bias : proc->rdq.cutoff;
     
    839658// try to pop from a lane given by index w
    840659static inline struct thread$ * try_pop(struct cluster * cltr, unsigned w __STATS(, __stats_readyQ_pop_t & stats)) with (cltr->ready_queue) {
    841         /* paranoid */ verify( w < lanes.count );
    842660        __STATS( stats.attempt++; )
    843661
     
    863681        // Actually pop the list
    864682        struct thread$ * thrd;
    865         #if defined(USE_AWARE_STEALING) || defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
     683        #if defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
    866684                unsigned long long tsc_before = ts(lane);
    867685        #endif
     
    879697        __STATS( stats.success++; )
    880698
    881         #if defined(USE_AWARE_STEALING) || defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
    882                 if (tsv != MAX) {
    883                         unsigned long long now = rdtscl();
    884                         unsigned long long pma = __atomic_load_n(&lanes.tscs[w].ma, __ATOMIC_RELAXED);
    885                         __atomic_store_n(&lanes.tscs[w].tv, tsv, __ATOMIC_RELAXED);
    886                         __atomic_store_n(&lanes.tscs[w].ma, moving_average(now, tsc_before, pma), __ATOMIC_RELAXED);
    887                 }
     699        #if defined(USE_WORK_STEALING) || defined(USE_CPU_WORK_STEALING)
     700                unsigned long long now = rdtscl();
     701                lanes.tscs[w].tv = tsv;
     702                lanes.tscs[w].ma = moving_average(now > tsc_before ? now - tsc_before : 0, lanes.tscs[w].ma);
    888703        #endif
    889704
    890         #if defined(USE_AWARE_STEALING) || defined(USE_CPU_WORK_STEALING)
     705        #if defined(USE_CPU_WORK_STEALING)
    891706                thrd->preferred = w / READYQ_SHARD_FACTOR;
    892707        #else
     
    987802                /* paranoid */ verifyf( it, "Unexpected null iterator, at index %u of %u\n", i, count);
    988803                it->rdq.id = value;
    989                 it->rdq.target = MAX;
     804                it->rdq.target = -1u;
    990805                value += READYQ_SHARD_FACTOR;
    991806                it = &(*it)`next;
     
    1000815
    1001816static void fix_times( struct cluster * cltr ) with( cltr->ready_queue ) {
    1002         #if defined(USE_AWARE_STEALING) || defined(USE_WORK_STEALING)
     817        #if defined(USE_WORK_STEALING)
    1003818                lanes.tscs = alloc(lanes.count, lanes.tscs`realloc);
    1004819                for(i; lanes.count) {
    1005                         lanes.tscs[i].tv = rdtscl();
    1006                         lanes.tscs[i].ma = 0;
     820                        unsigned long long tsc1 = ts(lanes.data[i]);
     821                        unsigned long long tsc2 = rdtscl();
     822                        lanes.tscs[i].tv = min(tsc1, tsc2);
    1007823                }
    1008824        #endif
     
    1050866                        // Update original
    1051867                        lanes.count = ncount;
    1052 
    1053                         lanes.caches = alloc( target, lanes.caches`realloc );
    1054868                }
    1055869
     
    1128942                                fix(lanes.data[idx]);
    1129943                        }
    1130 
    1131                         lanes.caches = alloc( target, lanes.caches`realloc );
    1132944                }
    1133945
    1134946                fix_times(cltr);
    1135 
    1136947
    1137948                reassign_cltr_id(cltr);
Note: See TracChangeset for help on using the changeset viewer.