75 |
75 |
|
76 |
76 |
import qualified Data.IntSet as IntSet
|
77 |
77 |
import Data.List
|
78 |
|
import Data.Maybe (fromJust)
|
|
78 |
import Data.Maybe (fromJust, isNothing)
|
79 |
79 |
import Data.Ord (comparing)
|
80 |
80 |
import Text.Printf (printf)
|
81 |
81 |
import Control.Monad
|
... | ... | |
93 |
93 |
|
94 |
94 |
-- | Allocation\/relocation solution.
|
95 |
95 |
data AllocSolution = AllocSolution
|
96 |
|
{ asFailures :: [FailMode] -- ^ Failure counts
|
97 |
|
, asAllocs :: Int -- ^ Good allocation count
|
98 |
|
, asSolutions :: [Node.AllocElement] -- ^ The actual result, length
|
99 |
|
-- of the list depends on the
|
100 |
|
-- allocation/relocation mode
|
101 |
|
, asLog :: [String] -- ^ A list of informational messages
|
|
96 |
{ asFailures :: [FailMode] -- ^ Failure counts
|
|
97 |
, asAllocs :: Int -- ^ Good allocation count
|
|
98 |
, asSolution :: Maybe Node.AllocElement -- ^ The actual allocation result
|
|
99 |
, asLog :: [String] -- ^ Informational messages
|
102 |
100 |
}
|
103 |
101 |
|
104 |
102 |
-- | Node evacuation/group change iallocator result type. This result
|
... | ... | |
125 |
123 |
-- | The empty solution we start with when computing allocations.
|
126 |
124 |
emptyAllocSolution :: AllocSolution
|
127 |
125 |
emptyAllocSolution = AllocSolution { asFailures = [], asAllocs = 0
|
128 |
|
, asSolutions = [], asLog = [] }
|
|
126 |
, asSolution = Nothing, asLog = [] }
|
129 |
127 |
|
130 |
128 |
-- | The empty evac solution.
|
131 |
129 |
emptyEvacSolution :: EvacSolution
|
... | ... | |
610 |
608 |
concatAllocs as (OpGood ns@(_, _, _, nscore)) =
|
611 |
609 |
let -- Choose the old or new solution, based on the cluster score
|
612 |
610 |
cntok = asAllocs as
|
613 |
|
osols = asSolutions as
|
|
611 |
osols = asSolution as
|
614 |
612 |
nsols = case osols of
|
615 |
|
[] -> [ns]
|
616 |
|
(_, _, _, oscore):[] ->
|
|
613 |
Nothing -> Just ns
|
|
614 |
Just (_, _, _, oscore) ->
|
617 |
615 |
if oscore < nscore
|
618 |
616 |
then osols
|
619 |
|
else [ns]
|
620 |
|
-- FIXME: here we simply concat to lists with more
|
621 |
|
-- than one element; we should instead abort, since
|
622 |
|
-- this is not a valid usage of this function
|
623 |
|
xs -> ns:xs
|
|
617 |
else Just ns
|
624 |
618 |
nsuc = cntok + 1
|
625 |
619 |
-- Note: we force evaluation of nsols here in order to keep the
|
626 |
620 |
-- memory profile low - we know that we will need nsols for sure
|
627 |
621 |
-- in the next cycle, so we force evaluation of nsols, since the
|
628 |
622 |
-- foldl' in the caller will only evaluate the tuple, but not the
|
629 |
623 |
-- elements of the tuple
|
630 |
|
in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolutions = nsols }
|
|
624 |
in nsols `seq` nsuc `seq` as { asAllocs = nsuc, asSolution = nsols }
|
631 |
625 |
|
632 |
626 |
-- | Given a solution, generates a reasonable description for it.
|
633 |
627 |
describeSolution :: AllocSolution -> String
|
634 |
628 |
describeSolution as =
|
635 |
629 |
let fcnt = asFailures as
|
636 |
|
sols = asSolutions as
|
|
630 |
sols = asSolution as
|
637 |
631 |
freasons =
|
638 |
632 |
intercalate ", " . map (\(a, b) -> printf "%s: %d" (show a) b) .
|
639 |
633 |
filter ((> 0) . snd) . collapseFailures $ fcnt
|
640 |
|
in if null sols
|
641 |
|
then "No valid allocation solutions, failure reasons: " ++
|
642 |
|
(if null fcnt
|
643 |
|
then "unknown reasons"
|
644 |
|
else freasons)
|
645 |
|
else let (_, _, nodes, cv) = head sols
|
646 |
|
in printf ("score: %.8f, successes %d, failures %d (%s)" ++
|
647 |
|
" for node(s) %s") cv (asAllocs as) (length fcnt) freasons
|
648 |
|
(intercalate "/" . map Node.name $ nodes)
|
|
634 |
in case sols of
|
|
635 |
Nothing -> "No valid allocation solutions, failure reasons: " ++
|
|
636 |
(if null fcnt then "unknown reasons" else freasons)
|
|
637 |
Just (_, _, nodes, cv) ->
|
|
638 |
printf ("score: %.8f, successes %d, failures %d (%s)" ++
|
|
639 |
" for node(s) %s") cv (asAllocs as) (length fcnt) freasons
|
|
640 |
(intercalate "/" . map Node.name $ nodes)
|
649 |
641 |
|
650 |
642 |
-- | Annotates a solution with the appropriate string.
|
651 |
643 |
annotateSolution :: AllocSolution -> AllocSolution
|
... | ... | |
725 |
717 |
fn accu (gdx, rasol) =
|
726 |
718 |
case rasol of
|
727 |
719 |
Bad _ -> accu
|
728 |
|
Ok sol | null (asSolutions sol) -> accu
|
|
720 |
Ok sol | isNothing (asSolution sol) -> accu
|
729 |
721 |
| unallocable gdx -> accu
|
730 |
722 |
| otherwise -> (gdx, sol):accu
|
731 |
723 |
|
... | ... | |
736 |
728 |
sortMGResults gl sols =
|
737 |
729 |
let extractScore (_, _, _, x) = x
|
738 |
730 |
solScore (gdx, sol) = (Group.allocPolicy (Container.find gdx gl),
|
739 |
|
(extractScore . head . asSolutions) sol)
|
|
731 |
(extractScore . fromJust . asSolution) sol)
|
740 |
732 |
in sortBy (comparing solScore) sols
|
741 |
733 |
|
742 |
734 |
-- | Finds the best group for an instance on a multi-group cluster.
|
... | ... | |
1150 |
1142 |
newlimit = fmap (flip (-) 1) limit
|
1151 |
1143 |
in case tryAlloc nl il newi2 allocnodes of
|
1152 |
1144 |
Bad s -> Bad s
|
1153 |
|
Ok (AllocSolution { asFailures = errs, asSolutions = sols3 }) ->
|
|
1145 |
Ok (AllocSolution { asFailures = errs, asSolution = sols3 }) ->
|
1154 |
1146 |
let newsol = Ok (collapseFailures errs, nl, il, ixes, cstats) in
|
1155 |
1147 |
case sols3 of
|
1156 |
|
[] -> newsol
|
1157 |
|
(xnl, xi, _, _):[] ->
|
|
1148 |
Nothing -> newsol
|
|
1149 |
Just (xnl, xi, _, _) ->
|
1158 |
1150 |
if limit == Just 0
|
1159 |
1151 |
then newsol
|
1160 |
1152 |
else iterateAlloc xnl (Container.add newidx xi il)
|
1161 |
1153 |
newlimit newinst allocnodes (xi:ixes)
|
1162 |
1154 |
(totalResources xnl:cstats)
|
1163 |
|
_ -> Bad "Internal error: multiple solutions for single\
|
1164 |
|
\ allocation"
|
1165 |
1155 |
|
1166 |
1156 |
-- | The core of the tiered allocation mode.
|
1167 |
1157 |
tieredAlloc :: Node.List
|