Skip to content

Instantly share code, notes, and snippets.

@wyoung
Created April 23, 2015 06:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wyoung/677a59e0043ac5533ee9 to your computer and use it in GitHub Desktop.
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
(***********************************************************************
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