Skip to content

Instantly share code, notes, and snippets.

@ybubnov
Created February 12, 2017 14:39
Show Gist options
  • Save ybubnov/e83e00d3cf5d54139e4433ff5786843b to your computer and use it in GitHub Desktop.
Save ybubnov/e83e00d3cf5d54139e4433ff5786843b to your computer and use it in GitHub Desktop.
Parallel conversion of the numbers to multiple radix
package main
import (
"fmt"
"runtime"
"strconv"
"strings"
)
const (
from = 2
to = 36
perline = 8
value = uint64(35)
)
// min returns the smaller of a or b.
func min(a, b int) int {
if a < b {
return a
}
return b
}
// tuple grounps the target radix and the value to convert.
type tuple struct {
value uint64
base int
}
// generator writes into the pipe channel a set of pairs of
// target radix plus the integer to convert.
func generator(from, to int, pipe chan<- tuple) {
defer close(pipe)
for from <= to {
pipe <- tuple{value, from}
from++
}
}
// converter reads the specified number of the integers from
// the ch channel, convers them according to the configuration
// and puts the result into the pipe channel.
func converter(n int, ch <-chan tuple, pipe chan<- string) {
for i := 0; i < n; i++ {
c := <-ch
pipe <- strconv.FormatUint(c.value, c.base)
}
}
// reducer groups the converted numbers into the comma-
// separated chunks and sends them into the pipe channel.
func reducer(n int, ch <-chan string, pipe chan<- string) {
var line []string
for i := 0; i < n; i++ {
line = append(line, <-ch)
if len(line) == perline || i+1 == n {
pipe <- strings.Join(line, ", ")
line = nil
}
}
}
// dumper prints the specified amount of the received strings
// from the channel ch to the standard output.
func dumper(n int, ch <-chan string) <-chan struct{} {
done := make(chan struct{})
go func() {
for i := 0; i < n; i++ {
fmt.Println(<-ch)
}
done <- struct{}{}
}()
return done
}
func main() {
// Spawn as much routines as number of CPUs are
// installed on the host (number can be less,
// and denepnds on the user inputs).
ncpu := runtime.NumCPU()
nconv := to - from + 1
// The channels to pipeline the output from the
// different routines.
convert := make(chan tuple)
reduce := make(chan string)
dump := make(chan string)
// Generate the pairs: target radix, number to
// convert.
go generator(from, to, convert)
nwork := min(ncpu, nconv)
for i := 0; i < nwork; i++ {
// Each routine will be assigned to process
// equal amount of the numbers, except the
// first one, which takes additional load.
n := nconv / nwork
if i == 0 {
n += (nconv % nwork)
}
go converter(n, convert, reduce)
}
// When host provides more CPUs, than lines should
// be printed, the amount of workers is reduced to
// the number of lines to print (e.g. 1 for 2 CPUs).
nline := nconv / perline
if nconv%perline != 0 {
nline++
}
nwork = min(ncpu, nline)
for i := 0; i < nwork; i++ {
// The reduce rountines have to print the even
// numbers of elements (per line). As in the
// previous algorithm, the first routine takes
// additional load.
n := perline * (nconv / perline / nwork)
if i == 0 {
n += (nconv - n*nwork)
}
go reducer(n, reduce, dump)
}
// Wait for the last routine, that prints the
// resulting comman-separated lines of numbers.
<-dumper(nline, dump)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment