sleepsort

command module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2022 License: MIT Imports: 17 Imported by: 0

README

Sleep Sort

My implementation of the Sleep Sort in Go.

Background

A friend introduced me to the concept of the sleep sort. After reading about it, I had to try implementing it in Go using Goroutines and channels. It ended up being one of my more fun toy projects and now I want to share.

Quick Start

Some sorts can take many minutes to complete. To exit early, use Ctrl+C.

To quickly execute using default values, use

```shell
go run . simple
go run . sequential

and

go run . concurrent

For help, execute with:

go run . --help

And for help specific to each command (sequential, concurrent, and simple), execute with (example):

go run . concurrent --help
Output

The output will contain the type of sort being executed along with the number of items and additional information if applicable to the sort type. A list of the unsorted items will be displayed and then the sort is executed.

Sleep sorting 100 items concurrently using 7 rounds
Unsorted items: [8081 7887 1847 4059 2081 1318 4425 2540 456 3300 694 8511 8162 5089 4728 3274 1211 1445 3237 9106 495 5466 1528 6258 8047 9947 8287 2888 2790 3015 5541 408 7387 6831 5429 5356 1737 631 1485 5026 6413 3090 5194 563 2433 4147 4078 4324 6159 1353 1957 3721 7189 2199 3000 8705 2888 4538 9703 9355 2451 8510 2605 156 8266 9828 5561 7202 4783 5746 1563 4376 9002 9718 5447 5094 1577 7463 7996 6420 8623 953 1137 3133 9241 59 3033 8643 3891 2002 8878 9336 2546 9107 7940 6503 552 9843 2205 1598]

When the sort is executed, multiple Goroutines will be launched. The display converts to a spinner to indicate the Goroutines are waited upon along with the number of Goroutines that are running. As the Goroutines complete, the displayed number of routines will decrement.

⣯ Waiting for 690 Goroutines

As the sorting "rounds" complete, the elapsed time is displayed along with a list of mismatches from the two most recent sorts.

Round 2 sorted in 10 seconds
Unsorted items (2):
8511 != 8510    8510 != 8511

Once the sort is complete, a list of sorted items is displayed along with an execution summary.

Sorted items: [59 156 408 456 495 552 563 631 694 953 1137 1211 1318 1353 1445 1485 1528 1563 1577 1598 1737 1847 1957 2002 2081 2199 2205 2433 2451 2540 2546 2605 2790 2888 2888 3000 3015 3033 3090 3133 3237 3274 3300 3721 3891 4059 4078 4147 4324 4376 4425 4538 4728 4783 5026 5089 5094 5194 5356 5429 5447 5466 5541 5561 5746 6159 6258 6413 6420 6503 6831 7189 7202 7387 7463 7887 7940 7996 8047 8081 8162 8266 8287 8510 8511 8623 8643 8705 8878 9002 9106 9107 9241 9336 9355 9703 9718 9828 9843 9947]
Sort complete in 40 seconds
Goroutines: 2

Basic Sort

The basic sort is performed with the SleepSort class. It takes in an array of uints and sorts it using sleeping Goroutines, each sleeping for a time equal to the uint value multiplied by one millisecond multiplied by a multiplier. The multiplier is a feature that is useful when the input values are closely spaced. Because of sleep sorting limitations, the output may not be fully sorted.

Simple Sort

The most simple implementation is with the SimpleSleepSort class. Reading the code for SimpleSleepSort is a good introduction to the basis for the more complex implementations. It sleep sorts using a single iteration (non-validating) and returns the sorted array. In the spirit of keeping the code minimal, the main package is implemented in simple/main.go with a hardcoded array of values and calls the Sort method to perform the sort. Execute it with:

go run ./simple/...

For completeness, the simple implementation is also included in the main program and can be ran with the simple command.

go run . simple

Validating Sorts

To address sleep sorting limitations, some validation functionality has been built on top of the basic sort. This validation executes the sort multiple times, each time using an increased multiplier value. After each sort, a comparison is made between the two most recent sorts and if they match, the result is returned. Because it's still possible that two sleep sorts could return invalid sorts, even a validating sort may return incorrect output. It's the nature of the sleep sort.

Sequential Validating Sort

The sequential validating sort launches the sleep sort routines sequentially, each time doubling the multiplier until the sorted output from two runs match. The exception is the first two sorts which are performed with a multiplier of 1. These sorts are executed concurrently then compared, after which each additional sort is performed sequentially. This will always yield a validated (but not guaranteed) sort because it loops until the two most recent runs match.

Execute this sort with a command such as:

go run sleepsort.go sequential --items 100
Concurrent Validating Sort

After coding the first two sorts to execute concurrently, I also realized that all sorts can be performed concurrently to save time. I quit working on the Sequential Validating Sort and began building the Concurrent Validating Sort.

This sort takes a number of rounds as input and launches a corresponding sort for each round, with each round having its multiplier doubled. The exception is the first round, of which two sorts are ran as Goroutines to quickly get an initial result to compare.

Unlike the sequential sort which will always yield a validated result, the maximum round count means two sorts may not pass the validation test by the time the maximum round count is reached and the sort will fail, returning an error.

Execute with:

go run sleepsort.go concurrent --items 100 --rounds 8

An interesting thing to keep mind about this implementation is the amount of Goroutines that are launched. By default, it launches 8 rounds (the first round is ran twice, then once for each additional round), each with 100 items in the list. That's 800 Goroutines. Add in the managing Goroutine for each round plus a few more for handling other things and it's running over 810 Goroutines with the number reducing as the result of each sleep rolls in. It's fun to launch it with settings such as 1,000 items and 20 Goroutines (go run . concurrent --items 1000 --rounds 20) and watch the number of mismatches slowly dwindle as the code arrives at an answer.

List Value Generation

By default the list of values is generated by an unseeded pseudo-random number generator from math/rand rather than crypto/rand. This helps with testing and experimentation because these values are repeatable. To seed the generator with the current time, use the --seed flag.

Runtime Metrics

This program implements the expvar package to provide runtime metrics. Access it with http://localhost:8080/debug/vars.

$ curl -s 127.0.0.1:8080/debug/vars | jq .
{
  "cmdline": [
    "/tmp/go-build127810536/b001/exe/sleepsort",
    "concurrent",
    "--items",
    "300"
  ],
  "memstats": {
    "Alloc": 2032560
...

Contributing

You'll surely spot defects (especially in the use of Goroutines and channels) and notice algorithm improvements or maybe even have a different technique other than sequential or concurrent. I'd like to see your PR.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL