[90ecade] | 1 | package main |
---|
| 2 | |
---|
| 3 | import ( |
---|
[2dd0689] | 4 | "context" |
---|
[90ecade] | 5 | "flag" |
---|
| 6 | "fmt" |
---|
| 7 | "math/rand" |
---|
| 8 | "os" |
---|
| 9 | "sync/atomic" |
---|
| 10 | "time" |
---|
[2dd0689] | 11 | "unsafe" |
---|
| 12 | "golang.org/x/sync/semaphore" |
---|
[90ecade] | 13 | "golang.org/x/text/language" |
---|
| 14 | "golang.org/x/text/message" |
---|
| 15 | ) |
---|
| 16 | |
---|
[2dd0689] | 17 | type GoCtx struct { |
---|
| 18 | s * semaphore.Weighted |
---|
| 19 | d * [] uint64 |
---|
[024fa4b] | 20 | c context.Context |
---|
[2dd0689] | 21 | } |
---|
| 22 | |
---|
| 23 | type Spot struct { |
---|
| 24 | ptr uintptr |
---|
| 25 | } |
---|
| 26 | |
---|
[024fa4b] | 27 | // Main handshake of the code |
---|
| 28 | // Single seat, first thread arriving waits |
---|
| 29 | // Next threads unblocks current one and blocks in its place |
---|
| 30 | // if share == true, exchange data in the process |
---|
| 31 | func (this * Spot) put( ctx * GoCtx, data * [] uint64, share bool) (* [] uint64) { |
---|
| 32 | new := uintptr(unsafe.Pointer(ctx)) |
---|
| 33 | // fmt.Printf("Enter with %p\n", data) |
---|
[2dd0689] | 34 | var raw uintptr |
---|
| 35 | for true { |
---|
| 36 | raw = this.ptr |
---|
| 37 | if raw == uintptr(1) { |
---|
| 38 | return nil |
---|
| 39 | } |
---|
[024fa4b] | 40 | if atomic.CompareAndSwapUintptr(&this.ptr, raw, new) { |
---|
[2dd0689] | 41 | break |
---|
| 42 | } |
---|
[90ecade] | 43 | } |
---|
| 44 | |
---|
[2dd0689] | 45 | if raw != uintptr(0) { |
---|
[024fa4b] | 46 | var val *GoCtx |
---|
| 47 | val = (*GoCtx)(unsafe.Pointer(raw)) |
---|
[90ecade] | 48 | if share { |
---|
[024fa4b] | 49 | val.d = data |
---|
[90ecade] | 50 | } |
---|
[2dd0689] | 51 | |
---|
| 52 | val.s.Release(1) |
---|
| 53 | } |
---|
| 54 | |
---|
[024fa4b] | 55 | ctx.s.Acquire(ctx.c, 1) |
---|
| 56 | // fmt.Printf("Leave with %p (was %p)\n", ctx.d, data) |
---|
| 57 | return ctx.d |
---|
[2dd0689] | 58 | } |
---|
| 59 | |
---|
| 60 | func (this * Spot) release() { |
---|
| 61 | val := (*GoCtx)(unsafe.Pointer(atomic.SwapUintptr(&this.ptr, uintptr(1)))) |
---|
| 62 | if val == nil { |
---|
| 63 | return |
---|
[90ecade] | 64 | } |
---|
[2dd0689] | 65 | |
---|
| 66 | val.s.Release(1) |
---|
[90ecade] | 67 | } |
---|
| 68 | |
---|
[fd84538] | 69 | func __xorshift64( state * uint64 ) (uint64) { |
---|
| 70 | x := *state |
---|
| 71 | x ^= x << 13 |
---|
| 72 | x ^= x >> 7 |
---|
| 73 | x ^= x << 17 |
---|
| 74 | *state = x |
---|
| 75 | return x |
---|
| 76 | } |
---|
[2dd0689] | 77 | |
---|
| 78 | func local(result chan uint64, start chan struct{}, size uint64, cnt uint64, channels [] Spot, share bool) { |
---|
[fd84538] | 79 | state := rand.Uint64() |
---|
[024fa4b] | 80 | var my_data [] uint64 |
---|
| 81 | my_data = make([]uint64, size) |
---|
[90ecade] | 82 | for i := uint64(0); i < size; i++ { |
---|
[024fa4b] | 83 | my_data[i] = 0 |
---|
[90ecade] | 84 | } |
---|
[024fa4b] | 85 | data := &my_data |
---|
[2dd0689] | 86 | |
---|
| 87 | sem := semaphore.NewWeighted(1) |
---|
| 88 | sem.Acquire(context.Background(), 1) |
---|
[024fa4b] | 89 | ctx := GoCtx{sem, data, context.Background()} |
---|
[2dd0689] | 90 | |
---|
[90ecade] | 91 | count := uint64(0) |
---|
| 92 | <- start |
---|
| 93 | for true { |
---|
| 94 | for i := uint64(0); i < cnt; i++ { |
---|
[024fa4b] | 95 | (*data)[__xorshift64(&state) % size] += 1 |
---|
[90ecade] | 96 | } |
---|
| 97 | |
---|
[fd84538] | 98 | i := __xorshift64(&state) % uint64(len(channels)) |
---|
[024fa4b] | 99 | // data = channels[i].put(sem, data, share) |
---|
| 100 | data = channels[i].put(&ctx, data, share) |
---|
[90ecade] | 101 | count += 1 |
---|
| 102 | |
---|
[2dd0689] | 103 | if clock_mode && atomic.LoadInt32(&stop) == 1 { break } |
---|
[90ecade] | 104 | if !clock_mode && count >= stop_count { break } |
---|
[024fa4b] | 105 | if uint64(len(*data)) != size { |
---|
[2dd0689] | 106 | panic("Data has weird size") |
---|
| 107 | } |
---|
[90ecade] | 108 | } |
---|
| 109 | |
---|
| 110 | atomic.AddInt64(&threads_left, -1); |
---|
| 111 | result <- count |
---|
| 112 | } |
---|
| 113 | |
---|
| 114 | func main() { |
---|
| 115 | work_sizeOpt := flag.Uint64("w", 2 , "Number of words (uint64) per threads") |
---|
| 116 | countOpt := flag.Uint64("c", 2 , "Number of words (uint64) to touch") |
---|
| 117 | shareOpt := flag.Bool ("s", false, "Pass the work data to the next thread when blocking") |
---|
| 118 | |
---|
[2dd0689] | 119 | defer bench_init()() |
---|
[90ecade] | 120 | |
---|
| 121 | size := *work_sizeOpt |
---|
| 122 | cnt := *countOpt |
---|
| 123 | share := *shareOpt |
---|
| 124 | |
---|
| 125 | if ! (nthreads > nprocs) { |
---|
| 126 | fmt.Fprintf(os.Stderr, "Must have more threads than procs\n") |
---|
| 127 | os.Exit(1) |
---|
| 128 | } |
---|
| 129 | |
---|
| 130 | barrierStart := make(chan struct{}) |
---|
| 131 | threads_left = int64(nthreads) |
---|
| 132 | result := make(chan uint64) |
---|
[2dd0689] | 133 | channels := make([]Spot, nthreads - nprocs) |
---|
[90ecade] | 134 | for i := range channels { |
---|
[2dd0689] | 135 | channels[i] = Spot{uintptr(0)} |
---|
[90ecade] | 136 | } |
---|
| 137 | |
---|
| 138 | for i := 0; i < nthreads; i++ { |
---|
[2dd0689] | 139 | go local(result, barrierStart, size, cnt, channels, share) |
---|
[90ecade] | 140 | } |
---|
| 141 | fmt.Printf("Starting\n"); |
---|
| 142 | |
---|
[2dd0689] | 143 | atomic.StoreInt32(&stop, 0) |
---|
[90ecade] | 144 | start := time.Now() |
---|
| 145 | close(barrierStart) |
---|
| 146 | |
---|
| 147 | wait(start, true); |
---|
| 148 | |
---|
[2dd0689] | 149 | atomic.StoreInt32(&stop, 1) |
---|
[90ecade] | 150 | end := time.Now() |
---|
| 151 | delta := end.Sub(start) |
---|
| 152 | |
---|
| 153 | fmt.Printf("\nDone\n") |
---|
| 154 | |
---|
[2dd0689] | 155 | for i := range channels { |
---|
| 156 | channels[i].release() |
---|
| 157 | } |
---|
| 158 | |
---|
[90ecade] | 159 | global_counter := uint64(0) |
---|
| 160 | for i := 0; i < nthreads; i++ { |
---|
[024fa4b] | 161 | r := <- result |
---|
| 162 | global_counter += r |
---|
| 163 | fmt.Printf("%d\n", r) |
---|
[90ecade] | 164 | } |
---|
| 165 | |
---|
| 166 | p := message.NewPrinter(language.English) |
---|
| 167 | p.Printf("Duration (ms) : %f\n", delta.Seconds()); |
---|
| 168 | p.Printf("Number of processors : %d\n", nprocs); |
---|
| 169 | p.Printf("Number of threads : %d\n", nthreads); |
---|
| 170 | p.Printf("Work size (64bit words): %d\n", size); |
---|
| 171 | p.Printf("Total Operations(ops) : %15d\n", global_counter) |
---|
| 172 | p.Printf("Ops per second : %18.2f\n", float64(global_counter) / delta.Seconds()) |
---|
| 173 | p.Printf("ns per ops : %18.2f\n", float64(delta.Nanoseconds()) / float64(global_counter)) |
---|
| 174 | p.Printf("Ops per threads : %15d\n", global_counter / uint64(nthreads)) |
---|
| 175 | p.Printf("Ops per procs : %15d\n", global_counter / uint64(nprocs)) |
---|
| 176 | p.Printf("Ops/sec/procs : %18.2f\n", (float64(global_counter) / float64(nprocs)) / delta.Seconds()) |
---|
| 177 | p.Printf("ns per ops/procs : %18.2f\n", float64(delta.Nanoseconds()) / (float64(global_counter) / float64(nprocs))) |
---|
| 178 | } |
---|