September 2017 Update
This commit is contained in:
parent
bba7bfd280
commit
ba8067c3b7
14570 changed files with 153136 additions and 63871 deletions
|
|
@ -5,7 +5,7 @@ import std.algorithm: min, max;
|
|||
import std.parallelism: task;
|
||||
import core.thread: Thread;
|
||||
import core.sync.mutex: Mutex;
|
||||
import core.time: dur;
|
||||
import core.time: seconds;
|
||||
|
||||
__gshared uint transfersCount;
|
||||
|
||||
|
|
@ -73,24 +73,22 @@ final class Buckets(size_t nBuckets) if (nBuckets > 0) {
|
|||
}
|
||||
|
||||
void randomize(size_t N)(Buckets!N data) {
|
||||
immutable maxi = data.length - 1;
|
||||
auto rng = Xorshift(1);
|
||||
|
||||
while (data.running) {
|
||||
immutable i = uniform(0, maxi, rng);
|
||||
immutable j = uniform(0, maxi, rng);
|
||||
immutable amount = uniform(0, 20, rng);
|
||||
immutable i = uniform(0, data.length, rng);
|
||||
immutable j = uniform(0, data.length, rng);
|
||||
immutable amount = uniform!"[]"(0, 20, rng);
|
||||
data.transfer(i, j, amount);
|
||||
}
|
||||
}
|
||||
|
||||
void equalize(size_t N)(Buckets!N data) {
|
||||
immutable maxi = data.length - 1;
|
||||
auto rng = Xorshift(1);
|
||||
|
||||
while (data.running) {
|
||||
immutable i = uniform(0, maxi, rng);
|
||||
immutable j = uniform(0, maxi, rng);
|
||||
immutable i = uniform(0, data.length, rng);
|
||||
immutable j = uniform(0, data.length, rng);
|
||||
immutable a = data[i];
|
||||
immutable b = data[j];
|
||||
if (a > b)
|
||||
|
|
@ -104,7 +102,7 @@ void display(size_t N)(Buckets!N data) {
|
|||
foreach (immutable _; 0 .. 10) {
|
||||
writeln(transfersCount, " ", data);
|
||||
transfersCount = 0;
|
||||
Thread.sleep(dur!"msecs"(1000));
|
||||
Thread.sleep(1.seconds);
|
||||
}
|
||||
data.running = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Data type required by task.
|
||||
type bucketList interface {
|
||||
// Two operations required by task. Updater parameter not specified
|
||||
// by task, but useful for displaying update counts as an indication
|
||||
// that transfer operations are happening "as often as possible."
|
||||
bucketValue(bucket int) int
|
||||
transfer(b1, b2, ammount, updater int)
|
||||
|
||||
// Operation not specified by task, but needed for synchronization.
|
||||
snapshot(bucketValues []int, transferCounts []int)
|
||||
|
||||
// Operation not specified by task, but useful.
|
||||
buckets() int // number of buckets
|
||||
}
|
||||
|
||||
// Total of all bucket values, declared as a const to demonstrate that
|
||||
// it doesn't change.
|
||||
const originalTotal = 1000
|
||||
|
||||
// Updater ids, used for maintaining transfer counts.
|
||||
const (
|
||||
idOrder = iota
|
||||
idChaos
|
||||
nUpdaters
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create a concrete object implementing the bucketList interface.
|
||||
bl := newChList(10, originalTotal, nUpdaters)
|
||||
|
||||
// Three concurrent tasks.
|
||||
go order(bl)
|
||||
go chaos(bl)
|
||||
buddha(bl)
|
||||
}
|
||||
|
||||
// The concurrent tasks exercise the data operations by going through
|
||||
// the bucketList interface. They do no explicit synchronization and
|
||||
// are not responsible for maintaining invariants.
|
||||
|
||||
// Exercise (1.) required by task: make values more equal.
|
||||
func order(bl bucketList) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
nBuckets := bl.buckets()
|
||||
for {
|
||||
b1 := r.Intn(nBuckets)
|
||||
b2 := r.Intn(nBuckets)
|
||||
v1 := bl.bucketValue(b1)
|
||||
v2 := bl.bucketValue(b2)
|
||||
if v1 > v2 {
|
||||
bl.transfer(b1, b2, (v1-v2)/2, idOrder)
|
||||
} else {
|
||||
bl.transfer(b2, b1, (v2-v1)/2, idOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise (2.) required by task: redistribute values.
|
||||
func chaos(bl bucketList) {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
nBuckets := bl.buckets()
|
||||
for {
|
||||
b1 := r.Intn(nBuckets)
|
||||
b2 := r.Intn(nBuckets)
|
||||
bl.transfer(b1, b2, r.Intn(bl.bucketValue(b1)+1), idChaos)
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise (3.) requred by task: display total.
|
||||
func buddha(bl bucketList) {
|
||||
nBuckets := bl.buckets()
|
||||
s := make([]int, nBuckets)
|
||||
tc := make([]int, nUpdaters)
|
||||
var total, nTicks int
|
||||
|
||||
fmt.Println("sum ---updates--- mean buckets")
|
||||
tr := time.Tick(time.Second / 10)
|
||||
for {
|
||||
var sum int
|
||||
<-tr
|
||||
bl.snapshot(s, tc)
|
||||
for _, l := range s {
|
||||
if l < 0 {
|
||||
panic("sob") // invariant not preserved
|
||||
}
|
||||
sum += l
|
||||
}
|
||||
// Output number of updates per tick and cummulative mean
|
||||
// updates per tick to demonstrate "as often as possible"
|
||||
// of task exercises 1 and 2.
|
||||
total += tc[0] + tc[1]
|
||||
nTicks++
|
||||
fmt.Printf("%d %6d %6d %7d %v\n", sum, tc[0], tc[1], total/nTicks, s)
|
||||
if sum != originalTotal {
|
||||
panic("weep") // invariant not preserved
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// chList (ch for channel-synchronized) is a concrete type implementing
|
||||
// the bucketList interface. The bucketList interface declared methods,
|
||||
// the struct type here declares data. chList methods are repsonsible
|
||||
// for synchronization so they are goroutine-safe. They are also
|
||||
// responsible for maintaining the invariants that the sum of all buckets
|
||||
// stays constant and that no bucket value goes negative.
|
||||
type chList struct {
|
||||
b []int // bucket data specified by task
|
||||
s chan bool // syncronization object
|
||||
tc []int // a transfer count for each updater
|
||||
}
|
||||
|
||||
// Constructor.
|
||||
func newChList(nBuckets, initialSum, nUpdaters int) *chList {
|
||||
bl := &chList{
|
||||
b: make([]int, nBuckets),
|
||||
s: make(chan bool, 1),
|
||||
tc: make([]int, nUpdaters),
|
||||
}
|
||||
// Distribute initialSum across buckets.
|
||||
for i, dist := nBuckets, initialSum; i > 0; {
|
||||
v := dist / i
|
||||
i--
|
||||
bl.b[i] = v
|
||||
dist -= v
|
||||
}
|
||||
// Synchronization is needed to maintain the invariant that the total
|
||||
// of all bucket values stays the same. This is an implementation of
|
||||
// the straightforward solution mentioned in the task description,
|
||||
// ensuring that only one transfer happens at a time. Channel s
|
||||
// holds a token. All methods must take the token from the channel
|
||||
// before accessing data and then return the token when they are done.
|
||||
// it is equivalent to a mutex. The constructor makes data available
|
||||
// by initially dropping the token in the channel after all data is
|
||||
// initialized.
|
||||
bl.s <- true
|
||||
return bl
|
||||
}
|
||||
|
||||
// Four methods implementing the bucketList interface.
|
||||
func (bl *chList) bucketValue(b int) int {
|
||||
<-bl.s // get token before accessing data
|
||||
r := bl.b[b]
|
||||
bl.s <- true // return token
|
||||
return r
|
||||
}
|
||||
|
||||
func (bl *chList) transfer(b1, b2, a int, ux int) {
|
||||
if b1 == b2 { // null operation
|
||||
return
|
||||
}
|
||||
// Get access.
|
||||
<-bl.s
|
||||
// Clamping maintains invariant that bucket values remain nonnegative.
|
||||
if a > bl.b[b1] {
|
||||
a = bl.b[b1]
|
||||
}
|
||||
// Transfer.
|
||||
bl.b[b1] -= a
|
||||
bl.b[b2] += a
|
||||
bl.tc[ux]++ // increment transfer count
|
||||
// Release "lock".
|
||||
bl.s <- true
|
||||
}
|
||||
|
||||
func (bl *chList) snapshot(s []int, tc []int) {
|
||||
<-bl.s
|
||||
copy(s, bl.b)
|
||||
copy(tc, bl.tc)
|
||||
for i := range bl.tc {
|
||||
bl.tc[i] = 0
|
||||
}
|
||||
bl.s <- true
|
||||
}
|
||||
|
||||
func (bl *chList) buckets() int {
|
||||
return len(bl.b)
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
// rwList, rw is for RWMutex-synchronized.
|
||||
type rwList struct {
|
||||
b []int // bucket data specified by task
|
||||
|
||||
// Syncronization objects.
|
||||
m []sync.Mutex // mutex for each bucket
|
||||
all sync.RWMutex // mutex for entire list, for snapshot operation
|
||||
|
||||
tc []int // a transfer count for each updater
|
||||
}
|
||||
|
||||
// Constructor.
|
||||
func newRwList(nBuckets, initialSum, nUpdaters int) *rwList {
|
||||
bl := &rwList{
|
||||
b: make([]int, nBuckets),
|
||||
m: make([]sync.Mutex, nBuckets),
|
||||
tc: make([]int, nUpdaters),
|
||||
}
|
||||
for i, dist := nBuckets, initialSum; i > 0; {
|
||||
v := dist / i
|
||||
i--
|
||||
bl.b[i] = v
|
||||
dist -= v
|
||||
}
|
||||
return bl
|
||||
}
|
||||
|
||||
// Four methods implementing the bucketList interface.
|
||||
func (bl *rwList) bucketValue(b int) int {
|
||||
bl.m[b].Lock() // lock on bucket ensures read is atomic
|
||||
r := bl.b[b]
|
||||
bl.m[b].Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
func (bl *rwList) transfer(b1, b2, a int, ux int) {
|
||||
if b1 == b2 { // null operation
|
||||
return
|
||||
}
|
||||
// RLock on list allows other simultaneous transfers.
|
||||
bl.all.RLock()
|
||||
// Locking lowest bucket first prevents deadlock
|
||||
// with multiple tasks working at the same time.
|
||||
if b1 < b2 {
|
||||
bl.m[b1].Lock()
|
||||
bl.m[b2].Lock()
|
||||
} else {
|
||||
bl.m[b2].Lock()
|
||||
bl.m[b1].Lock()
|
||||
}
|
||||
// clamp
|
||||
if a > bl.b[b1] {
|
||||
a = bl.b[b1]
|
||||
}
|
||||
// transfer
|
||||
bl.b[b1] -= a
|
||||
bl.b[b2] += a
|
||||
bl.tc[ux]++ // increment transfer count
|
||||
// release
|
||||
bl.m[b1].Unlock()
|
||||
bl.m[b2].Unlock()
|
||||
bl.all.RUnlock()
|
||||
// With current Go, the program can hang without a call to gosched here.
|
||||
// It seems that functions in the sync package don't touch the scheduler,
|
||||
// (which is good) but we need to touch it here to give the channel
|
||||
// operations in buddha a chance to run. (The current Go scheduler
|
||||
// is basically cooperative rather than preemptive.)
|
||||
runtime.Gosched()
|
||||
}
|
||||
|
||||
func (bl *rwList) snapshot(s []int, tc []int) {
|
||||
bl.all.Lock() // RW lock on list prevents transfers during snap.
|
||||
copy(s, bl.b)
|
||||
copy(tc, bl.tc)
|
||||
for i := range bl.tc {
|
||||
bl.tc[i] = 0
|
||||
}
|
||||
bl.all.Unlock()
|
||||
}
|
||||
|
||||
func (bl *rwList) buckets() int {
|
||||
return len(bl.b)
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
// lf for lock-free
|
||||
type lfList struct {
|
||||
b []int32
|
||||
sync.RWMutex
|
||||
tc []int
|
||||
}
|
||||
|
||||
// Constructor.
|
||||
func newLfList(nBuckets, initialSum, nUpdaters int) *lfList {
|
||||
bl := &lfList{
|
||||
b: make([]int32, nBuckets),
|
||||
tc: make([]int, nUpdaters),
|
||||
}
|
||||
for i, dist := int32(nBuckets), int32(initialSum); i > 0; {
|
||||
v := dist / i
|
||||
i--
|
||||
bl.b[i] = v
|
||||
dist -= v
|
||||
}
|
||||
return bl
|
||||
}
|
||||
|
||||
// Four methods implementing the bucketList interface.
|
||||
func (bl *lfList) bucketValue(b int) int {
|
||||
return int(atomic.LoadInt32(&bl.b[b]))
|
||||
}
|
||||
|
||||
func (bl *lfList) transfer(b1, b2, a int, ux int) {
|
||||
if b1 == b2 {
|
||||
return
|
||||
}
|
||||
bl.RLock()
|
||||
for {
|
||||
t := int32(a)
|
||||
v1 := atomic.LoadInt32(&bl.b[b1])
|
||||
if t > v1 {
|
||||
t = v1
|
||||
}
|
||||
if atomic.CompareAndSwapInt32(&bl.b[b1], v1, v1-t) {
|
||||
atomic.AddInt32(&bl.b[b2], t)
|
||||
break
|
||||
}
|
||||
// else retry
|
||||
}
|
||||
bl.tc[ux]++
|
||||
bl.RUnlock()
|
||||
runtime.Gosched()
|
||||
}
|
||||
|
||||
func (bl *lfList) snapshot(s []int, tc []int) {
|
||||
bl.Lock()
|
||||
for i, bv := range bl.b {
|
||||
s[i] = int(bv)
|
||||
}
|
||||
for i := range bl.tc {
|
||||
tc[i], bl.tc[i] = bl.tc[i], 0
|
||||
}
|
||||
bl.Unlock()
|
||||
}
|
||||
|
||||
func (bl *lfList) buckets() int {
|
||||
return len(bl.b)
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
// mnList (mn for monitor-synchronized) is a concrete type implementing
|
||||
// the bucketList interface. The monitor is a goroutine, all communication
|
||||
// with it is done through channels, which are the members of mnList.
|
||||
// All data implementing the buckets is encapsulated in the monitor.
|
||||
type mnList struct {
|
||||
vrCh chan *valueReq
|
||||
trCh chan *transferReq
|
||||
srCh chan *snapReq
|
||||
nbCh chan chan int
|
||||
}
|
||||
|
||||
// Constructor makes channels and starts monitor.
|
||||
func newMnList(nBuckets, initialSum, nUpdaters int) *mnList {
|
||||
mn := &mnList{
|
||||
make(chan *valueReq),
|
||||
make(chan *transferReq),
|
||||
make(chan *snapReq),
|
||||
make(chan chan int),
|
||||
}
|
||||
go monitor(mn, nBuckets, initialSum, nUpdaters)
|
||||
return mn
|
||||
}
|
||||
|
||||
// Monitor goroutine ecapsulates data and enters a loop to handle requests.
|
||||
// The loop handles one request at a time, thus serializing all access.
|
||||
func monitor(mn *mnList, nBuckets, initialSum, nUpdaters int) {
|
||||
// bucket representation
|
||||
b := make([]int, nBuckets)
|
||||
for i, dist := nBuckets, initialSum; i > 0; {
|
||||
v := dist / i
|
||||
i--
|
||||
b[i] = v
|
||||
dist -= v
|
||||
}
|
||||
// transfer count representation
|
||||
count := make([]int, nUpdaters)
|
||||
|
||||
// monitor loop
|
||||
for {
|
||||
select {
|
||||
// value request operation
|
||||
case vr := <-mn.vrCh:
|
||||
vr.resp <- b[vr.bucket]
|
||||
|
||||
// transfer operation
|
||||
case tr := <-mn.trCh:
|
||||
// clamp
|
||||
if tr.amount > b[tr.from] {
|
||||
tr.amount = b[tr.from]
|
||||
}
|
||||
// transfer
|
||||
b[tr.from] -= tr.amount
|
||||
b[tr.to] += tr.amount
|
||||
count[tr.updaterId]++
|
||||
|
||||
// snap operation
|
||||
case sr := <-mn.srCh:
|
||||
copy(sr.bucketSnap, b)
|
||||
copy(sr.countSnap, count)
|
||||
for i := range count {
|
||||
count[i] = 0
|
||||
}
|
||||
sr.resp <- true
|
||||
|
||||
// number of buckets
|
||||
case nb := <-mn.nbCh:
|
||||
nb <- nBuckets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type valueReq struct {
|
||||
bucket int
|
||||
resp chan int
|
||||
}
|
||||
|
||||
func (mn *mnList) bucketValue(b int) int {
|
||||
resp := make(chan int)
|
||||
mn.vrCh <- &valueReq{b, resp}
|
||||
return <-resp
|
||||
}
|
||||
|
||||
type transferReq struct {
|
||||
from, to int
|
||||
amount int
|
||||
updaterId int
|
||||
}
|
||||
|
||||
func (mn *mnList) transfer(b1, b2, a, ux int) {
|
||||
mn.trCh <- &transferReq{b1, b2, a, ux}
|
||||
}
|
||||
|
||||
type snapReq struct {
|
||||
bucketSnap []int
|
||||
countSnap []int
|
||||
resp chan bool
|
||||
}
|
||||
|
||||
func (mn *mnList) snapshot(s []int, tc []int) {
|
||||
resp := make(chan bool)
|
||||
mn.srCh <- &snapReq{s, tc, resp}
|
||||
<-resp
|
||||
}
|
||||
|
||||
func (mn *mnList) buckets() int {
|
||||
resp := make(chan int)
|
||||
mn.nbCh <- resp
|
||||
return <-resp
|
||||
}
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
public class AtomicUpdates
|
||||
{
|
||||
public static class Buckets
|
||||
{
|
||||
private final int[] data;
|
||||
|
||||
public Buckets(int[] data)
|
||||
{
|
||||
this.data = data.clone();
|
||||
}
|
||||
|
||||
public int getBucket(int index)
|
||||
{
|
||||
synchronized (data)
|
||||
{ return data[index]; }
|
||||
}
|
||||
|
||||
public int transfer(int srcBucketIndex, int destBucketIndex, int amount)
|
||||
{
|
||||
if (amount == 0)
|
||||
return 0;
|
||||
// Negative transfers will happen in the opposite direction
|
||||
if (amount < 0)
|
||||
{
|
||||
int tempIndex = srcBucketIndex;
|
||||
srcBucketIndex = destBucketIndex;
|
||||
destBucketIndex = tempIndex;
|
||||
amount = -amount;
|
||||
}
|
||||
synchronized (data)
|
||||
{
|
||||
if (amount > data[srcBucketIndex])
|
||||
amount = data[srcBucketIndex];
|
||||
if (amount <= 0)
|
||||
return 0;
|
||||
data[srcBucketIndex] -= amount;
|
||||
data[destBucketIndex] += amount;
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getBuckets()
|
||||
{
|
||||
synchronized (data)
|
||||
{ return data.clone(); }
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTotal(int[] values)
|
||||
{
|
||||
int totalValue = 0;
|
||||
for (int i = values.length - 1; i >= 0; i--)
|
||||
totalValue += values[i];
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
final int NUM_BUCKETS = 10;
|
||||
Random rnd = new Random();
|
||||
final int[] values = new int[NUM_BUCKETS];
|
||||
for (int i = 0; i < values.length; i++)
|
||||
values[i] = rnd.nextInt(10);
|
||||
System.out.println("Initial Array: " + getTotal(values) + " " + Arrays.toString(values));
|
||||
final Buckets buckets = new Buckets(values);
|
||||
|
||||
new Thread(new Runnable() {
|
||||
public void run()
|
||||
{
|
||||
Random r = new Random();
|
||||
while (true)
|
||||
{
|
||||
int srcBucketIndex = r.nextInt(NUM_BUCKETS);
|
||||
int destBucketIndex = r.nextInt(NUM_BUCKETS);
|
||||
int amount = (buckets.getBucket(srcBucketIndex) - buckets.getBucket(destBucketIndex)) >> 1;
|
||||
if (amount != 0)
|
||||
buckets.transfer(srcBucketIndex, destBucketIndex, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
).start();
|
||||
|
||||
new Thread(new Runnable() {
|
||||
public void run()
|
||||
{
|
||||
Random r = new Random();
|
||||
while (true)
|
||||
{
|
||||
int srcBucketIndex = r.nextInt(NUM_BUCKETS);
|
||||
int destBucketIndex = r.nextInt(NUM_BUCKETS);
|
||||
int srcBucketAmount = buckets.getBucket(srcBucketIndex);
|
||||
int destBucketAmount = buckets.getBucket(destBucketIndex);
|
||||
int amount = r.nextInt(srcBucketAmount + destBucketAmount + 1) - destBucketAmount;
|
||||
if (amount != 0)
|
||||
buckets.transfer(srcBucketIndex, destBucketIndex, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
).start();
|
||||
|
||||
while (true)
|
||||
{
|
||||
long nextPrintTime = System.currentTimeMillis() + 3000;
|
||||
long curTime;
|
||||
while ((curTime = System.currentTimeMillis()) < nextPrintTime)
|
||||
{
|
||||
try
|
||||
{ Thread.sleep(nextPrintTime - curTime); }
|
||||
catch (InterruptedException e)
|
||||
{ }
|
||||
}
|
||||
int[] bucketValues = buckets.getBuckets();
|
||||
System.out.println("Current values: " + getTotal(bucketValues) + " " + Arrays.toString(bucketValues));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface Buckets {
|
||||
public static Buckets new_(int[] data) {
|
||||
return $.new_(data);
|
||||
}
|
||||
public static void main(String... arguments) {
|
||||
$.main(arguments);
|
||||
}
|
||||
public int[] getBuckets();
|
||||
|
||||
public int getBucket(int index);
|
||||
|
||||
public int transfer(int srcBucketIndex, int destBucketIndex, int amount);
|
||||
|
||||
public enum $ {
|
||||
$$;
|
||||
|
||||
private static Buckets new_(int[] data) {
|
||||
return (FunctionalBuckets) function -> {
|
||||
synchronized (data) {
|
||||
return Optional.of(data)
|
||||
.map(function)
|
||||
.filter(output -> output != data)
|
||||
.orElseGet(() -> data.clone())
|
||||
;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void main(String... arguments) {
|
||||
Stream.of(new Random())
|
||||
.parallel()
|
||||
.map(r -> r.ints($.NUM_BUCKETS, 0, NUM_BUCKETS))
|
||||
.map(IntStream::toArray)
|
||||
.peek(bucketValues -> Stream.of(bucketValues)
|
||||
.map(values -> "Initial values: " + getTotal(values) + " " + Arrays.toString(values))
|
||||
.forEach(System.out::println)
|
||||
)
|
||||
.map(Buckets::new_)
|
||||
.forEach(
|
||||
Stream.<Consumer<Buckets>>of(
|
||||
$::processBuckets,
|
||||
$::displayBuckets
|
||||
).reduce($ -> {}, Consumer::andThen)
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private static interface FunctionalBuckets extends Buckets {
|
||||
public Object untypedUseData(Function<int[], Object> function);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public default <OUTPUT> OUTPUT useData(Function<int[], OUTPUT> function) {
|
||||
return (OUTPUT) untypedUseData(function::apply);
|
||||
}
|
||||
|
||||
@Override
|
||||
public default int[] getBuckets() {
|
||||
return useData(Function.<int[]>identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public default int getBucket(int index) {
|
||||
return useData(data -> data[index]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public default int transfer(int originalSrcBucketIndex, int originalDestBucketIndex, int originalAmount) {
|
||||
return useData(data -> {
|
||||
int srcBucketIndex = originalSrcBucketIndex;
|
||||
int destBucketIndex = originalDestBucketIndex;
|
||||
int amount = originalAmount;
|
||||
if (amount == 0) {
|
||||
return 0;
|
||||
}
|
||||
// Negative transfers will happen in the opposite direction
|
||||
if (amount < 0) {
|
||||
int tempIndex = srcBucketIndex;
|
||||
srcBucketIndex = destBucketIndex;
|
||||
destBucketIndex = tempIndex;
|
||||
amount = -amount;
|
||||
}
|
||||
if (amount > data[srcBucketIndex]) {
|
||||
amount = data[srcBucketIndex];
|
||||
}
|
||||
if (amount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
data[srcBucketIndex] -= amount;
|
||||
data[destBucketIndex] += amount;
|
||||
return amount;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final int NUM_BUCKETS = 10;
|
||||
private static final int PRINT_DELAY = 3_000;
|
||||
|
||||
private static int getTotal(int[] values) {
|
||||
return Arrays.stream(values)
|
||||
.parallel()
|
||||
.sum()
|
||||
;
|
||||
}
|
||||
|
||||
private static void equalizeBuckets(Buckets buckets) {
|
||||
Random r = new Random();
|
||||
while (true) {
|
||||
int srcBucketIndex = r.nextInt($.NUM_BUCKETS);
|
||||
int destBucketIndex = r.nextInt($.NUM_BUCKETS);
|
||||
Stream.of(srcBucketIndex, destBucketIndex)
|
||||
.map(buckets::getBucket)
|
||||
.reduce((srcBucketAmount, destBucketAmount) ->
|
||||
srcBucketAmount - destBucketAmount
|
||||
)
|
||||
.map(amount -> amount >> 1)
|
||||
.filter(amount -> amount != 0)
|
||||
.ifPresent(amount ->
|
||||
buckets.transfer(srcBucketIndex, destBucketIndex, amount)
|
||||
)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private static void randomizeBuckets (Buckets buckets) {
|
||||
Random r = new Random();
|
||||
while (true) {
|
||||
int srcBucketIndex = r.nextInt($.NUM_BUCKETS);
|
||||
int destBucketIndex = r.nextInt($.NUM_BUCKETS);
|
||||
Stream.of(srcBucketIndex, destBucketIndex)
|
||||
.map(buckets::getBucket)
|
||||
.reduce((srcBucketAmount, destBucketAmount) ->
|
||||
r.nextInt(srcBucketAmount + destBucketAmount + 1) - destBucketAmount
|
||||
)
|
||||
.filter(amount -> amount != 0)
|
||||
.ifPresent(amount ->
|
||||
buckets.transfer(srcBucketIndex, destBucketIndex, amount)
|
||||
)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private static Runnable run(Runnable runnable) {
|
||||
return runnable;
|
||||
}
|
||||
|
||||
private static void processBuckets(Buckets buckets) {
|
||||
Stream.<Consumer<Buckets>>of(
|
||||
$::equalizeBuckets,
|
||||
$::randomizeBuckets
|
||||
)
|
||||
.parallel()
|
||||
.map(consumer -> run(() -> consumer.accept(buckets)))
|
||||
.map(Thread::new)
|
||||
.forEach(Thread::start)
|
||||
;
|
||||
}
|
||||
|
||||
private static void displayBuckets(Buckets buckets) {
|
||||
while (true) {
|
||||
long nextPrintTime = System.currentTimeMillis() + PRINT_DELAY;
|
||||
long curTime;
|
||||
while ((curTime = System.currentTimeMillis()) < nextPrintTime) {
|
||||
try {
|
||||
Thread.sleep(nextPrintTime - curTime);
|
||||
}
|
||||
catch (InterruptedException e) {}
|
||||
}
|
||||
Stream.of(buckets)
|
||||
.parallel()
|
||||
.map(Buckets::getBuckets)
|
||||
.map(values -> "Current values: " + getTotal(values) + " " + Arrays.toString(values))
|
||||
.forEach(System.out::println)
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
Task/Atomic-updates/Java/atomic-updates.java
Normal file
106
Task/Atomic-updates/Java/atomic-updates.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
import java.util.Arrays;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class AtomicUpdates {
|
||||
|
||||
private static final int NUM_BUCKETS = 10;
|
||||
|
||||
public static class Buckets {
|
||||
private final int[] data;
|
||||
|
||||
public Buckets(int[] data) {
|
||||
this.data = data.clone();
|
||||
}
|
||||
|
||||
public int getBucket(int index) {
|
||||
synchronized (data) {
|
||||
return data[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int transfer(int srcIndex, int dstIndex, int amount) {
|
||||
if (amount < 0)
|
||||
throw new IllegalArgumentException("negative amount: " + amount);
|
||||
if (amount == 0)
|
||||
return 0;
|
||||
|
||||
synchronized (data) {
|
||||
if (data[srcIndex] - amount < 0)
|
||||
amount = data[srcIndex];
|
||||
if (data[dstIndex] + amount < 0)
|
||||
amount = Integer.MAX_VALUE - data[dstIndex];
|
||||
if (amount < 0)
|
||||
throw new IllegalStateException();
|
||||
data[srcIndex] -= amount;
|
||||
data[dstIndex] += amount;
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getBuckets() {
|
||||
synchronized (data) {
|
||||
return data.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long getTotal(int[] values) {
|
||||
long total = 0;
|
||||
for (int value : values) {
|
||||
total += value;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
|
||||
int[] values = new int[NUM_BUCKETS];
|
||||
for (int i = 0; i < values.length; i++)
|
||||
values[i] = rnd.nextInt() & Integer.MAX_VALUE;
|
||||
System.out.println("Initial Array: " + getTotal(values) + " " + Arrays.toString(values));
|
||||
|
||||
Buckets buckets = new Buckets(values);
|
||||
new Thread(() -> equalize(buckets), "equalizer").start();
|
||||
new Thread(() -> transferRandomAmount(buckets), "transferrer").start();
|
||||
new Thread(() -> print(buckets), "printer").start();
|
||||
}
|
||||
|
||||
private static void transferRandomAmount(Buckets buckets) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
while (true) {
|
||||
int srcIndex = rnd.nextInt(NUM_BUCKETS);
|
||||
int dstIndex = rnd.nextInt(NUM_BUCKETS);
|
||||
int amount = rnd.nextInt() & Integer.MAX_VALUE;
|
||||
buckets.transfer(srcIndex, dstIndex, amount);
|
||||
}
|
||||
}
|
||||
|
||||
private static void equalize(Buckets buckets) {
|
||||
ThreadLocalRandom rnd = ThreadLocalRandom.current();
|
||||
while (true) {
|
||||
int srcIndex = rnd.nextInt(NUM_BUCKETS);
|
||||
int dstIndex = rnd.nextInt(NUM_BUCKETS);
|
||||
int amount = (buckets.getBucket(srcIndex) - buckets.getBucket(dstIndex)) / 2;
|
||||
if (amount >= 0)
|
||||
buckets.transfer(srcIndex, dstIndex, amount);
|
||||
}
|
||||
}
|
||||
|
||||
private static void print(Buckets buckets) {
|
||||
while (true) {
|
||||
long nextPrintTime = System.currentTimeMillis() + 3000;
|
||||
long now;
|
||||
while ((now = System.currentTimeMillis()) < nextPrintTime) {
|
||||
try {
|
||||
Thread.sleep(nextPrintTime - now);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int[] bucketValues = buckets.getBuckets();
|
||||
System.out.println("Current values: " + getTotal(bucketValues) + " " + Arrays.toString(bucketValues));
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Task/Atomic-updates/Zkl/atomic-updates-1.zkl
Normal file
29
Task/Atomic-updates/Zkl/atomic-updates-1.zkl
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
class B{
|
||||
const N=10;
|
||||
var [const]
|
||||
buckets=(1).pump(N,List).copy(), //(1,2,3...)
|
||||
lock=Atomic.Lock(), cnt=Atomic.Int();
|
||||
fcn init{ "Initial sum: ".println(values().sum()); }
|
||||
fcn transferArb{ // transfer arbitary amount from 1 bucket to another
|
||||
b1:=(0).random(N); b2:=(0).random(N);
|
||||
critical(lock){
|
||||
t:=(0).random(buckets[b1]);
|
||||
buckets[b1]=buckets[b1]-t; buckets[b2]=buckets[b2]+t;
|
||||
}
|
||||
cnt.inc();
|
||||
}
|
||||
fcn transferEq{ // try to make two buckets equal
|
||||
b1:=(0).random(N); b2:=(0).random(N);
|
||||
critical(lock){
|
||||
v1:=buckets[b1]; v2:=buckets[b2];
|
||||
t:=(v1-v2).abs()/2;
|
||||
if (v1<v2) t = -t;
|
||||
buckets[b1]=v1-t; buckets[b2]=v2+t;
|
||||
}
|
||||
cnt.inc();
|
||||
}
|
||||
fcn values{ critical(lock){buckets.copy()} }
|
||||
}
|
||||
|
||||
fcn threadA(b){ while(1) { b.transferArb(); } }
|
||||
fcn threadE(b){ while(1) { b.transferEq(); } }
|
||||
9
Task/Atomic-updates/Zkl/atomic-updates-2.zkl
Normal file
9
Task/Atomic-updates/Zkl/atomic-updates-2.zkl
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
b:=B();
|
||||
do(10){ threadA.launch(b); } do(10){ threadE.launch(b); }
|
||||
|
||||
while(1){
|
||||
v:=b.values();
|
||||
v.println("-->",v.sum()," ", b.cnt.value," transfers ",
|
||||
vm.numThreads," threads");
|
||||
Atomic.sleep(2.5);
|
||||
}
|
||||
30
Task/Atomic-updates/Zkl/atomic-updates-3.zkl
Normal file
30
Task/Atomic-updates/Zkl/atomic-updates-3.zkl
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
class C{
|
||||
const N=10;
|
||||
var [const]
|
||||
buckets=(1).pump(N,List).copy(), //(1,2,3...)
|
||||
pipe = Thread.Pipe(), cnt=Atomic.Int();
|
||||
fcn init{
|
||||
pipe.write(buckets);
|
||||
"Initial sum: ".println(values().sum());
|
||||
}
|
||||
fcn transferArb{ // transfer arbitary amount from 1 bucket to another
|
||||
b1:=(0).random(N); b2:=(0).random(N);
|
||||
v:=pipe.read();
|
||||
t:=(0).random(v[b1]); v[b1]=v[b1]-t; v[b2]=v[b2]+t;
|
||||
pipe.write(v);
|
||||
cnt.inc();
|
||||
}
|
||||
fcn transferEq{ // try to make two buckets equal
|
||||
b1:=(0).random(N); b2:=(0).random(N);
|
||||
v:=pipe.read();
|
||||
v1:=v[b1]; v2:=v[b2]; t:=(v1-v2).abs()/2;
|
||||
if (v1<v2) t = -t;
|
||||
v[b1]=v1-t; v[b2]=v2+t;
|
||||
pipe.write(v);
|
||||
cnt.inc();
|
||||
}
|
||||
fcn values{
|
||||
v:=pipe.read(); v2:=v.copy(); pipe.write(v);
|
||||
v2;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue