2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -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. 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>
|
||||
|
|
|
|||
150
Task/Atomic-updates/Go/atomic-updates.go
Normal file
150
Task/Atomic-updates/Go/atomic-updates.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
69
Task/Atomic-updates/Perl-6/atomic-updates.pl6
Normal file
69
Task/Atomic-updates/Perl-6/atomic-updates.pl6
Normal 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;
|
||||
}
|
||||
}
|
||||
73
Task/Atomic-updates/Rust/atomic-updates.rust
Normal file
73
Task/Atomic-updates/Rust/atomic-updates.rust
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue