2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -1,6 +1,7 @@
Define a data type consisting of a fixed number of 'buckets', each containing a nonnegative integer value, which supports operations to
;Task:
Define a data type consisting of a fixed number of 'buckets', each containing a nonnegative integer value, which supports operations to:
# get the current value of any bucket
# remove a specified amount from one specified bucket and add it to another, preserving the total of all bucket values, and [[wp:Clamping (graphics)|clamping]] the transferred amount to ensure the values remain nonnegative
# remove a specified amount from one specified bucket and add it to another, preserving the total of all bucket values, and [[wp:Clamping (graphics)|clamping]] the transferred amount to ensure the values remain non-negative
----
@ -9,8 +10,10 @@ In order to exercise this data type, create one set of buckets, and start three
# As often as possible, pick two buckets and arbitrarily redistribute their values.
# At whatever rate is convenient, display (by any means) the total value and, optionally, the individual values of each bucket.
<br>
The display task need not be explicit; use of e.g. a debugger or trace tool is acceptable provided it is simple to set up to provide the display.
----
This task is intended as an exercise in ''atomic'' operations. The sum of the bucket values must be preserved even if the two tasks attempt to perform transfers simultaneously, and a straightforward solution is to ensure that at any time, only one transfer is actually occurring — that the transfer operation is ''atomic''.
This task is intended as an exercise in ''atomic'' operations. &nbsp; The sum of the bucket values must be preserved even if the two tasks attempt to perform transfers simultaneously, and a straightforward solution is to ensure that at any time, only one transfer is actually occurring — that the transfer operation is ''atomic''.
<br><br>

View file

@ -0,0 +1,150 @@
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
const nBuckets = 10
type bucketList struct {
b [nBuckets]int // bucket data specified by task
// transfer counts for each updater, not strictly required by task but
// useful to show that the two updaters get fair chances to run.
tc [2]int
sync.Mutex // synchronization
}
// Updater ids, to track number of transfers by updater.
// these can index bucketlist.tc for example.
const (
idOrder = iota
idChaos
)
const initialSum = 1000 // sum of all bucket values
// Constructor.
func newBucketList() *bucketList {
var bl bucketList
// Distribute initialSum across buckets.
for i, dist := nBuckets, initialSum; i > 0; {
v := dist / i
i--
bl.b[i] = v
dist -= v
}
return &bl
}
// method 1 required by task, get current value of a bucket
func (bl *bucketList) bucketValue(b int) int {
bl.Lock() // lock before accessing data
r := bl.b[b]
bl.Unlock()
return r
}
// method 2 required by task
func (bl *bucketList) transfer(b1, b2, a int, ux int) {
// Get access.
bl.Lock()
// 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
bl.Unlock()
}
// additional useful method
func (bl *bucketList) snapshot(s *[nBuckets]int, tc *[2]int) {
bl.Lock()
*s = bl.b
*tc = bl.tc
bl.tc = [2]int{} // clear transfer counts
bl.Unlock()
}
var bl = newBucketList()
func main() {
// Three concurrent tasks.
go order() // make values closer to equal
go chaos() // arbitrarily redistribute values
buddha() // display total value and individual values of each bucket
}
// The concurrent tasks exercise the data operations by calling bucketList
// methods. The bucketList methods are "threadsafe", by which we really mean
// goroutine-safe. The conconcurrent tasks then do no explicit synchronization
// and are not responsible for maintaining invariants.
// Exercise 1 required by task: make values more equal.
func order() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for {
b1 := r.Intn(nBuckets)
b2 := r.Intn(nBuckets - 1)
if b2 >= b1 {
b2++
}
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() {
r := rand.New(rand.NewSource(time.Now().Unix()))
for {
b1 := r.Intn(nBuckets)
b2 := r.Intn(nBuckets - 1)
if b2 >= b1 {
b2++
}
bl.transfer(b1, b2, r.Intn(bl.bucketValue(b1)+1), idChaos)
}
}
// Exercise 3 requred by task: display total.
func buddha() {
var s [nBuckets]int
var tc [2]int
var total, nTicks int
fmt.Println("sum ---updates--- mean buckets")
tr := time.Tick(time.Second / 10)
for {
<-tr
bl.snapshot(&s, &tc)
var sum int
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 %3d\n", sum, tc[0], tc[1], total/nTicks, s)
if sum != initialSum {
panic("weep") // invariant not preserved
}
}
}

View file

@ -0,0 +1,69 @@
#| A collection of non-negative integers, with atomic operations.
class BucketStore {
has $.elems is required;
has @!buckets = ^1024 .pick xx $!elems;
has $lock = Lock.new;
#| Returns an array with the contents of all buckets.
method buckets {
$lock.protect: { [@!buckets] }
}
#| Transfers $amount from bucket at index $from, to bucket at index $to.
method transfer ($amount, :$from!, :$to!) {
return if $from == $to;
$lock.protect: {
my $clamped = $amount min @!buckets[$from];
@!buckets[$from] -= $clamped;
@!buckets[$to] += $clamped;
}
}
}
# Create bucket store
my $bucket-store = BucketStore.new: elems => 8;
my $initial-sum = $bucket-store.buckets.sum;
# Start a thread to equalize buckets
Thread.start: {
loop {
my @buckets = $bucket-store.buckets;
# Pick 2 buckets, so that $to has not more than $from
my ($to, $from) = @buckets.keys.pick(2).sort({ @buckets[$_] });
# Transfer half of the difference, rounded down
$bucket-store.transfer: ([-] @buckets[$from, $to]) div 2, :$from, :$to;
}
}
# Start a thread to distribute values among buckets
Thread.start: {
loop {
my @buckets = $bucket-store.buckets;
# Pick 2 buckets
my ($to, $from) = @buckets.keys.pick(2);
# Transfer a random portion
$bucket-store.transfer: ^@buckets[$from] .pick, :$from, :$to;
}
}
# Loop to display buckets
loop {
sleep 1;
my @buckets = $bucket-store.buckets;
my $sum = @buckets.sum;
say "{@buckets.fmt: '%4d'}, total $sum";
if $sum != $initial-sum {
note "ERROR: Total changed from $initial-sum to $sum";
exit 1;
}
}

View file

@ -0,0 +1,73 @@
extern crate rand;
use std::sync::{Arc, Mutex};
use std::thread;
use std::cmp;
use std::time::Duration;
use rand::Rng;
use rand::distributions::{IndependentSample, Range};
trait Buckets {
fn equalize<R:Rng>(&mut self, rng: &mut R);
fn randomize<R:Rng>(&mut self, rng: &mut R);
fn print_state(&self);
}
impl Buckets for [i32] {
fn equalize<R:Rng>(&mut self, rng: &mut R) {
let range = Range::new(0,self.len()-1);
let src = range.ind_sample(rng);
let dst = range.ind_sample(rng);
if dst != src {
let amount = cmp::min(((dst + src) / 2) as i32, self[src]);
let multiplier = if amount >= 0 { -1 } else { 1 };
self[src] += amount * multiplier;
self[dst] -= amount * multiplier;
}
}
fn randomize<R:Rng>(&mut self, rng: &mut R) {
let ind_range = Range::new(0,self.len()-1);
let src = ind_range.ind_sample(rng);
let dst = ind_range.ind_sample(rng);
if dst != src {
let amount = cmp::min(Range::new(0,20).ind_sample(rng), self[src]);
self[src] -= amount;
self[dst] += amount;
}
}
fn print_state(&self) {
println!("{:?} = {}", self, self.iter().sum::<i32>());
}
}
fn main() {
let e_buckets = Arc::new(Mutex::new([10; 10]));
let r_buckets = e_buckets.clone();
let p_buckets = e_buckets.clone();
thread::spawn(move || {
let mut rng = rand::thread_rng();
loop {
let mut buckets = e_buckets.lock().unwrap();
buckets.equalize(&mut rng);
}
});
thread::spawn(move || {
let mut rng = rand::thread_rng();
loop {
let mut buckets = r_buckets.lock().unwrap();
buckets.randomize(&mut rng);
}
});
let sleep_time = Duration::new(1,0);
loop {
{
let buckets = p_buckets.lock().unwrap();
buckets.print_state();
}
thread::sleep(sleep_time);
}
}