|
| 1 | +-------------------- MODULE BlockingQueuePoisonApple_stats ------------------- |
| 2 | +EXTENDS BlockingQueuePoisonApple, CSV, TLC, TLCExt, Integers, IOUtils |
| 3 | + |
| 4 | +Max(a,b) == |
| 5 | + IF a > b THEN a ELSE b |
| 6 | + |
| 7 | +Min(a,b) == |
| 8 | + IF a < b THEN a ELSE b |
| 9 | + |
| 10 | +----------------------------------------------------------------------------- |
| 11 | + |
| 12 | +\* Collecting statistics does not work with an ordinary TLA+ spec such as |
| 13 | +\* BlockingQueuePoisonApple because of its non-determinism. The statistics |
| 14 | +\* would be all over the board and not meaningful. Instead, we break the |
| 15 | +\* non-determinism by mimiking a real-world workload s.t. each Producer adds |
| 16 | +\* a (fixed) number of elements to the queue. In other words, we remove |
| 17 | +\* those behaviors from the state space that we do not expect to see in the |
| 18 | +\* executions of an implementation of the TLA+ spec. |
| 19 | +\* It would be more convenient to conjoin a liveness property that stipulates |
| 20 | +\* that each producer adds N elements to the queue instead of amending the |
| 21 | +\* original next-state relation Next with SNext below. However, I don't |
| 22 | +\* see a way to get around adding the (auxiliary) pending variable that keeps |
| 23 | +\* track of the number of elements added by each producer. |
| 24 | +\* Alternatively, a more variable approach, compared to producers adding a |
| 25 | +\* fixed number of elements to the queue, would be to add probabilities that |
| 26 | +\* model the likelyhood of a Put and Terminate actions to occur (with |
| 27 | +\* Put having a much higher probability). This is straighforward to model |
| 28 | +\* with TLC's RandomElement operator by conjoining it to Put and |
| 29 | +\* Terminate: |
| 30 | +\* RandomElement(1..10) \in 1..9 |
| 31 | +\* and |
| 32 | +\* RandomElement(1..10) \in 10..10 |
| 33 | +\* A more sophisticated example is at |
| 34 | +\* https://github.com/lemmy/PageQueue/blob/master/PageQueue.tla |
| 35 | + |
| 36 | +\* Number of elements to put into the queue by each producer. |
| 37 | +CONSTANT Work |
| 38 | +ASSUME Work \in (Nat \ {0}) |
| 39 | + |
| 40 | +\* Count how many elements have been added by each producer. This variable |
| 41 | +\* is how we model workloads. |
| 42 | +VARIABLES pending |
| 43 | +auxVars == <<pending>> |
| 44 | + |
| 45 | +\* The two auxilary variables are used to measure the over- and under-provisioning |
| 46 | +\* of consumers. |
| 47 | +\* TODO: Make Welford's algorithm for variance and standard deviation available |
| 48 | +\* here (https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) |
| 49 | +\* It would free users from doing things in R, ... , and probably be useful |
| 50 | +\* in 99% of collecting statistics. |
| 51 | +VARIABLE over, under |
| 52 | +statsVars == <<over, under>> |
| 53 | + |
| 54 | +STypeInv == |
| 55 | + /\ pending \in [ Producers -> 0..Work ] |
| 56 | + /\ over \in Int |
| 57 | + /\ under \in Int |
| 58 | + |
| 59 | +SInit == |
| 60 | + \* Verbatim copy (redundant) of the original spec. |
| 61 | + /\ buffer = <<>> |
| 62 | + /\ waitSet = {} |
| 63 | + /\ cons = [ c \in Consumers |-> Cardinality(Producers) ] |
| 64 | + /\ prod = [ p \in Producers |-> Cardinality(Consumers) ] |
| 65 | + \* Workload: |
| 66 | + /\ pending = [ p \in Producers |-> Work ] |
| 67 | + \* Statistics: |
| 68 | + /\ over = 0 |
| 69 | + /\ under = 0 |
| 70 | + |
| 71 | +SNext == |
| 72 | + /\ \/ \E p \in Producers: /\ Put(p, p) |
| 73 | + \* Decrement pending iff the buffer changed. |
| 74 | + /\ IF buffer # buffer' |
| 75 | + THEN pending' = [pending EXCEPT ![p]=@-1] |
| 76 | + ELSE UNCHANGED pending |
| 77 | + \/ \E c \in Consumers: /\ Get(c) |
| 78 | + /\ UNCHANGED pending |
| 79 | + \* Update the statistics. |
| 80 | + /\ over' = Max(over, Cardinality(ac) - Cardinality(ap)) |
| 81 | + /\ under' = Min(under, Cardinality(ac) - Cardinality(ap)) |
| 82 | + |
| 83 | +StatsSpec == |
| 84 | + SInit /\ [][SNext]_<<vars, auxVars, statsVars>> |
| 85 | + |
| 86 | +----------------------------------------------------------------------------- |
| 87 | + |
| 88 | +CONSTANT CSVFile |
| 89 | + |
| 90 | +StatsInv == |
| 91 | + \* Per-behavior stats written to CSVfile on global termination. Global |
| 92 | + \* termination is defined by the union of the active producers and consumers |
| 93 | + \* being the empty set. |
| 94 | + /\ (ap \cup ac = {}) => |
| 95 | + CSVWrite("%1$s,%2$s,%3$s,%4$s,%5$s,%6$s,%7$s", |
| 96 | + << |
| 97 | + \* TLCGet("stats").traces equals the number of traces generated |
| 98 | + \* so far. Thus, individual records in the CSV can be aggregated |
| 99 | + \* later. TLCGet("level") can be seen as the timestamp of the |
| 100 | + \* record. |
| 101 | + TLCGet("stats").traces, TLCGet("level"), |
| 102 | + \* |
| 103 | + Cardinality(Producers), Cardinality(Consumers), BufCapacity, |
| 104 | + \* ...the actual statistics. |
| 105 | + over, under |
| 106 | + >>, CSVFile) |
| 107 | + |
| 108 | +----------------------------------------------------------------------------- |
| 109 | + |
| 110 | +\* Below, we read the values from the OS environment and turn them into constants |
| 111 | +\* of this spec. The operators s2n and SetOfNModelValues should probably be |
| 112 | +\* moved into IOUtils. |
| 113 | + |
| 114 | +s2n(str) == |
| 115 | + CHOOSE n \in 0..2^16: ToString(n) = str |
| 116 | + |
| 117 | +SetOfNModelValues(lbl, N) == |
| 118 | + { TLCModelValue(lbl \o ToString(i)) : i \in 1..N } |
| 119 | + |
| 120 | +----------------------------------------------------------------------------- |
| 121 | + |
| 122 | +B == |
| 123 | + s2n(IOEnv.B) |
| 124 | + |
| 125 | +P == |
| 126 | + SetOfNModelValues("p", s2n(IOEnv.P)) |
| 127 | + |
| 128 | +C == |
| 129 | + SetOfNModelValues("c", s2n(IOEnv.C)) |
| 130 | + |
| 131 | +W == |
| 132 | + s2n(IOEnv.W) |
| 133 | + |
| 134 | +Out == |
| 135 | + IOEnv.Out |
| 136 | + |
| 137 | +=========================================================================== |
| 138 | + |
| 139 | +--------------------- CONFIG BlockingQueuePoisonApple_stats --------------------- |
| 140 | +CONSTANTS |
| 141 | + BufCapacity <- B |
| 142 | + Producers <- P |
| 143 | + Consumers <- C |
| 144 | + Work <- W |
| 145 | + CSVFile <- Out |
| 146 | + Poison = Poison |
| 147 | + |
| 148 | +SPECIFICATION StatsSpec |
| 149 | + |
| 150 | +CHECK_DEADLOCK FALSE |
| 151 | + |
| 152 | +INVARIANT STypeInv |
| 153 | + |
| 154 | +INVARIANT StatsInv |
| 155 | + |
| 156 | +============================================================================= |
0 commit comments