authorgravatar for kbutcher6200@gmail.comkprotty <kbutcher6200@gmail.com> 2019-12-17 08:57:07-06:00
committergravatar for kbutcher6200@gmail.comkprotty <kbutcher6200@gmail.com> 2019-12-17 15:38:00-06:00
logc9122964436b16dd44a5fb8dfd92f0768ad6fef3
tree43a293be46eef6d84725cea2e86b9ce5759f7a61
parent26e08d57016352c469ae34b3fbd61e7f2a85a76c

SpinLock: loopHint & yield distinction


3 files changed, 25 insertions(+), 26 deletions(-)

lib/std/mutex.zig+6-10
...@@ -75,7 +75,7 @@ else if (builtin.os == .windows)...@@ -75,7 +75,7 @@ else if (builtin.os == .windows)
7575
76 fn acquireSlow(self: *Mutex) Held {76 fn acquireSlow(self: *Mutex) Held {
77 @setCold(true);77 @setCold(true);
78 while (true) : (SpinLock.yield(1)) {78 while (true) : (SpinLock.loopHint(1)) {
79 const waiters = @atomicLoad(u32, &self.waiters, .Monotonic);79 const waiters = @atomicLoad(u32, &self.waiters, .Monotonic);
8080
81 // try and take lock if unlocked81 // try and take lock if unlocked
...@@ -99,7 +99,7 @@ else if (builtin.os == .windows)...@@ -99,7 +99,7 @@ else if (builtin.os == .windows)
99 // unlock without a rmw/cmpxchg instruction99 // unlock without a rmw/cmpxchg instruction
100 @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release);100 @atomicStore(u8, @ptrCast(*u8, &self.mutex.locked), 0, .Release);
101101
102 while (true) : (SpinLock.yield(1)) {102 while (true) : (SpinLock.loopHint(1)) {
103 const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);103 const waiters = @atomicLoad(u32, &self.mutex.waiters, .Monotonic);
104 104
105 // no one is waiting105 // no one is waiting
...@@ -142,10 +142,6 @@ else if (builtin.link_libc or builtin.os == .linux)...@@ -142,10 +142,6 @@ else if (builtin.link_libc or builtin.os == .linux)
142 self.* = undefined;142 self.* = undefined;
143 }143 }
144144
145 fn yield() void {
146 os.sched_yield() catch SpinLock.yield(30);
147 }
148
149 pub fn tryAcquire(self: *Mutex) ?Held {145 pub fn tryAcquire(self: *Mutex) ?Held {
150 if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)146 if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic) != null)
151 return null;147 return null;
...@@ -175,7 +171,7 @@ else if (builtin.link_libc or builtin.os == .linux)...@@ -175,7 +171,7 @@ else if (builtin.link_libc or builtin.os == .linux)
175 } else if (state & QUEUE_MASK == 0) {171 } else if (state & QUEUE_MASK == 0) {
176 break;172 break;
177 }173 }
178 yield();174 SpinLock.yield();
179 state = @atomicLoad(usize, &self.state, .Monotonic);175 state = @atomicLoad(usize, &self.state, .Monotonic);
180 }176 }
181177
...@@ -198,7 +194,7 @@ else if (builtin.link_libc or builtin.os == .linux)...@@ -198,7 +194,7 @@ else if (builtin.link_libc or builtin.os == .linux)
198 break;194 break;
199 };195 };
200 }196 }
201 yield();197 SpinLock.yield();
202 state = @atomicLoad(usize, &self.state, .Monotonic);198 state = @atomicLoad(usize, &self.state, .Monotonic);
203 }199 }
204 }200 }
...@@ -225,7 +221,7 @@ else if (builtin.link_libc or builtin.os == .linux)...@@ -225,7 +221,7 @@ else if (builtin.link_libc or builtin.os == .linux)
225 // try and lock the LFIO queue to pop a node off,221 // try and lock the LFIO queue to pop a node off,
226 // stopping altogether if its already locked or the queue is empty222 // stopping altogether if its already locked or the queue is empty
227 var state = @atomicLoad(usize, &self.state, .Monotonic);223 var state = @atomicLoad(usize, &self.state, .Monotonic);
228 while (true) : (std.SpinLock.yield(1)) {224 while (true) : (SpinLock.loopHint(1)) {
229 if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0)225 if (state & QUEUE_LOCK != 0 or state & QUEUE_MASK == 0)
230 return;226 return;
231 state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break;227 state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break;
...@@ -234,7 +230,7 @@ else if (builtin.link_libc or builtin.os == .linux)...@@ -234,7 +230,7 @@ else if (builtin.link_libc or builtin.os == .linux)
234 // acquired the QUEUE_LOCK, try and pop a node to wake it.230 // acquired the QUEUE_LOCK, try and pop a node to wake it.
235 // if the mutex is locked, then unset QUEUE_LOCK and let231 // if the mutex is locked, then unset QUEUE_LOCK and let
236 // the thread who holds the mutex do the wake-up on unlock()232 // the thread who holds the mutex do the wake-up on unlock()
237 while (true) : (std.SpinLock.yield(1)) {233 while (true) : (SpinLock.loopHint(1)) {
238 if ((state & MUTEX_LOCK) != 0) {234 if ((state & MUTEX_LOCK) != 0) {
239 state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return;235 state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Acquire) orelse return;
240 } else {236 } else {
lib/std/reset_event.zig+2-5
...@@ -234,10 +234,7 @@ const AtomicEvent = struct {...@@ -234,10 +234,7 @@ const AtomicEvent = struct {
234 timer = time.Timer.start() catch unreachable;234 timer = time.Timer.start() catch unreachable;
235235
236 while (@atomicLoad(i32, ptr, .Acquire) == expected) {236 while (@atomicLoad(i32, ptr, .Acquire) == expected) {
237 switch (builtin.os) {237 SpinLock.yield();
238 .windows => SpinLock.yield(400),
239 else => os.sched_yield() catch SpinLock.yield(1),
240 }
241 if (timeout) |timeout_ns| {238 if (timeout) |timeout_ns| {
242 if (timer.read() >= timeout_ns)239 if (timer.read() >= timeout_ns)
243 return error.TimedOut;240 return error.TimedOut;
...@@ -320,7 +317,7 @@ const AtomicEvent = struct {...@@ -320,7 +317,7 @@ const AtomicEvent = struct {
320 return @intToPtr(?windows.HANDLE, handle);317 return @intToPtr(?windows.HANDLE, handle);
321 },318 },
322 LOADING => {319 LOADING => {
323 SpinLock.yield(1000);320 SpinLock.yield();
324 handle = @atomicLoad(usize, &event_handle, .Monotonic);321 handle = @atomicLoad(usize, &event_handle, .Monotonic);
325 },322 },
326 else => {323 else => {
lib/std/spinlock.zig+17-11
...@@ -35,27 +35,33 @@ pub const SpinLock = struct {...@@ -35,27 +35,33 @@ pub const SpinLock = struct {
35 pub fn acquire(self: *SpinLock) Held {35 pub fn acquire(self: *SpinLock) Held {
36 while (true) {36 while (true) {
37 return self.tryAcquire() orelse {37 return self.tryAcquire() orelse {
38 // On native windows, SwitchToThread is too expensive,38 yield();
39 // and yielding for 380-410 iterations was found to be
40 // a nice sweet spot. Posix systems on the other hand,
41 // especially linux, perform better by yielding the thread.
42 switch (builtin.os) {
43 .windows => yield(400),
44 else => std.os.sched_yield() catch yield(1),
45 }
46 continue;39 continue;
47 };40 };
48 }41 }
49 }42 }
5043
44 pub fn yield() void {
45 // On native windows, SwitchToThread is too expensive,
46 // and yielding for 380-410 iterations was found to be
47 // a nice sweet spot. Posix systems on the other hand,
48 // especially linux, perform better by yielding the thread.
49 switch (builtin.os) {
50 .windows => loopHint(400),
51 else => std.os.sched_yield() catch loopHint(1),
52 }
53 }
54
51 /// Hint to the cpu that execution is spinning55 /// Hint to the cpu that execution is spinning
52 /// for the given amount of iterations.56 /// for the given amount of iterations.
53 pub fn yield(iterations: usize) void {57 pub fn loopHint(iterations: usize) void {
54 var i = iterations;58 var i = iterations;
55 while (i != 0) : (i -= 1) {59 while (i != 0) : (i -= 1) {
56 switch (builtin.arch) {60 switch (builtin.arch) {
57 .i386, .x86_64 => asm volatile ("pause"),61 // these instructions use a memory clobber as they
58 .arm, .aarch64 => asm volatile ("yield"),62 // flush the pipeline of any speculated reads/writes.
63 .i386, .x86_64 => asm volatile ("pause" ::: "memory"),
64 .arm, .aarch64 => asm volatile ("yield" ::: "memory"),
59 else => std.os.sched_yield() catch {},65 else => std.os.sched_yield() catch {},
60 }66 }
61 }67 }