Created
April 23, 2015 06:45
-
-
Save wyoung/677a59e0043ac5533ee9 to your computer and use it in GitHub Desktop.
Parallel F# version of fsckrelprimes; verbose mode and progress reporting removed, since parallel status output gets confusing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(*********************************************************************** | |
fsckrelprime.fs - A simulator for fsck frequency on ext[234] sytems | |
which shows that using relatively-prime max-mount-count values | |
gives a lower chance of multi-volume checks on reboot. See the | |
usage message for more details. | |
***********************************************************************) | |
open System | |
//// usage /////////////////////////////////////////////////////////// | |
// Tells how to run the program, then exits. | |
let usage() = | |
let appName = AppDomain.CurrentDomain.FriendlyName.Replace(".exe", "") | |
printfn """usage: %s <threads> <numbers...> | |
Multiplies all passed numbers together, then iterates from 1 to | |
their product looking for values that have two or more values in | |
the passed set that are evenly divisible by two or more of those | |
same values. | |
You will find that if you pass purely random numbers that you will | |
get a higher collision rate than if you pass prime numbers or | |
/relatively/ prime numbers: http://goo.gl/bQbu5Z | |
The application is ext[234] fsck frequency on a system with multiple | |
volumes that are set to be checked periodically. With the same | |
value, they all have to be checked when any has to be checked, as | |
long as the mount/check frequency is the same for all. With random | |
values, you can get into cases where two or more volumes have to be | |
checked at the same time uncomfortably often. With relatively prime | |
values, you minimize the chance of two or more volumes needing to be | |
checked at the same time.""" appName | |
exit 1 | |
//// main ////////////////////////////////////////////////////////////// | |
[<EntryPoint>] | |
let main argv = | |
// Parse the value set from the command line | |
let mutable max = 1UL | |
let vals = | |
try Array.map uint64 argv | |
with _ -> eprintfn "ERROR: Bad value on command line."; exit 2 | |
for n in vals do max <- max * uint64(n) | |
if vals.Length < 2 || max < 1UL then usage() | |
// The simulator core | |
let fmax = float(max) | |
let sim subset ibase range = async { | |
let prevPctReport = ref 0 | |
let buckets = ref (Array.zeroCreate (vals.Length - 1)) | |
let localMax = ibase + range | |
let frange = float(range) | |
let i = ref 0UL | |
while !i <= range do | |
// F# "for...to" loops can only be indexed by 32-bit ints, | |
// yet max can be well into the billions and beyond. Thus | |
// the awkward looping construct. | |
i := !i + 1UL | |
// Count the number of times i divides evenly into vals, and | |
// keep track of how many times each multi-volume event occurs. | |
let divs = Array.filter (fun e -> (!i + ibase) % e = 0UL) vals | |
let b = divs.Length - 2 | |
if b >= 0 then (!buckets).[b] <- (!buckets).[b] + 1 | |
// Return the simluator's results | |
return !buckets | |
} | |
// Run N copies of the simulator over N subranges. This may skip | |
// calculating up to (threads - 1) possibilities at the tail end, | |
// but this won't affect the reported results as long as max is at | |
// least 1,000, since we report only 3 significant digits below. | |
let threads = 4 | |
let subsetSize = max / uint64(threads) | |
let simulatorBuckets = | |
seq { 1 .. threads } | |
|> Seq.map (fun i -> sim i (uint64(i - 1) * subsetSize) subsetSize) | |
|> Async.Parallel | |
|> Async.RunSynchronously | |
// Add up the corresponding values from each subrange, yielding a | |
// single bucket array. | |
let addArrayColumns a b = | |
Array.zip a b |> Array.map (fun (a', b') -> a' + b') | |
let combinedBuckets = Array.reduce addArrayColumns simulatorBuckets | |
// Report results | |
printfn "Results:\n" | |
printfn " period: %s" (max.ToString("N0")) | |
let calcPct e = float(e) / fmax * 100.0 | |
let results = Array.map calcPct combinedBuckets | |
let total = Array.reduce (+) results | |
for i = 0 to results.Length - 1 do | |
printfn " %d-volume: %3.1f%%" (i + 2) results.[i] | |
printfn " total: %.1f%%\n" total | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment