When an idle thread tries to steal work from a sibling's remaining toSearch queue, always try to split along a path boundary. This avoids missing delta opportunities in the current window of the thread whose work is being taken. The search order is reversed to walk further down the chain from current position, avoiding the risk of splitting the list within the path the thread is currently processing. When selecting which thread to split from use an accurate estimate of the size to be taken. This avoids selecting a thread that has only one path remaining but may contain more pending entries than another thread with several paths remaining. As there is now a race condition where the straggling thread can start the next path before the split can finish, the stealWork() loop spins until it is able to acquire a split or there is only one path remaining in the siblings. Change-Id: Ib11ff99f90a4d9efab24bf4a85342cc63203dba5tags/v3.0.0.201305080800-m7
@@ -76,23 +76,24 @@ final class DeltaTask implements Callable<Object> { | |||
} | |||
synchronized Slice stealWork() { | |||
for (int attempts = 0; attempts < 2; attempts++) { | |||
for (;;) { | |||
DeltaTask maxTask = null; | |||
Slice maxSlice = null; | |||
int maxWork = 0; | |||
for (DeltaTask task : tasks) { | |||
int r = task.remaining(); | |||
if (maxWork < r) { | |||
Slice s = task.remaining(); | |||
if (s != null && maxWork < s.size()) { | |||
maxTask = task; | |||
maxWork = r; | |||
maxSlice = s; | |||
maxWork = s.size(); | |||
} | |||
} | |||
if (maxTask == null) | |||
return null; | |||
Slice s = maxTask.stealWork(); | |||
if (s != null) | |||
return s; | |||
if (maxTask.tryStealWork(maxSlice)) | |||
return maxSlice; | |||
} | |||
return null; | |||
} | |||
} | |||
@@ -104,6 +105,10 @@ final class DeltaTask implements Callable<Object> { | |||
beginIndex = b; | |||
endIndex = e; | |||
} | |||
final int size() { | |||
return endIndex - beginIndex; | |||
} | |||
} | |||
private final Block block; | |||
@@ -131,13 +136,13 @@ final class DeltaTask implements Callable<Object> { | |||
return null; | |||
} | |||
int remaining() { | |||
Slice remaining() { | |||
DeltaWindow d = dw; | |||
return d != null ? d.remaining() : 0; | |||
return d != null ? d.remaining() : null; | |||
} | |||
Slice stealWork() { | |||
boolean tryStealWork(Slice s) { | |||
DeltaWindow d = dw; | |||
return d != null ? d.stealWork() : null; | |||
return d != null ? d.tryStealWork(s) : false; | |||
} | |||
} |
@@ -115,26 +115,37 @@ final class DeltaWindow { | |||
res = DeltaWindowEntry.createWindow(config.getDeltaSearchWindowSize()); | |||
} | |||
synchronized int remaining() { | |||
return end - cur; | |||
} | |||
synchronized DeltaTask.Slice stealWork() { | |||
synchronized DeltaTask.Slice remaining() { | |||
int e = end; | |||
int n = (e - cur) >>> 1; | |||
if (0 == n) | |||
int halfRemaining = (e - cur) >>> 1; | |||
if (0 == halfRemaining) | |||
return null; | |||
int t = e - n; | |||
int h = toSearch[t].getPathHash(); | |||
while (cur < t) { | |||
if (h == toSearch[t - 1].getPathHash()) | |||
t--; | |||
else | |||
break; | |||
int split = e - halfRemaining; | |||
int h = toSearch[split].getPathHash(); | |||
// Attempt to split on the next path after the 50% split point. | |||
for (int n = split + 1; n < e; n++) { | |||
if (h != toSearch[n].getPathHash()) | |||
return new DeltaTask.Slice(n, e); | |||
} | |||
end = t; | |||
return new DeltaTask.Slice(t, e); | |||
if (h != toSearch[cur].getPathHash()) { | |||
// Try to split on the path before the 50% split point. | |||
// Do not split the path currently being processed. | |||
for (int p = split - 1; cur < p; p--) { | |||
if (h != toSearch[p].getPathHash()) | |||
return new DeltaTask.Slice(p + 1, e); | |||
} | |||
} | |||
return null; | |||
} | |||
synchronized boolean tryStealWork(DeltaTask.Slice s) { | |||
if (s.beginIndex <= cur) | |||
return false; | |||
end = s.beginIndex; | |||
return true; | |||
} | |||
void search() throws IOException { |