out-of-memory errors


  • Functional language
  • Proven-correct compiler
  • Able to bootstrap itself

<->

🤝*

.asm


    fun yes = (print "yes"; yes)
  

💥?

<->

🤝-{💥}

.asm

Objectives


Why do programs run out of memory?


-> dataLang



         v = Number  int
           | Word64  word64
           | Block   ts tag (v list)  -- ts = tag = num
           | CodePtr num
           | RefPtr  num
    

[1,2,3]



      Block 67 cons_tag [Number 1;
        Block 66 cons_tag [Number 2;
          Block 65 cons_tag [Number 3;
            Block 0 nil_tag []]]]
    
is_space_safe(dataLang)
==>
sem(CakeML)

<->

sem(machine)

    size_of_heap s  <= s.limits.heap_limit

    size_of_stack s <= s.limits.stack_limit
  

How do we count?

size_of_heap s
=
size_of reachabe_values s.refs {}


       <|s.locals|> ++ <|s.stack|> ++ <|s.global|>
      

size_of_heap s =
size_of_heap (run_gc s)

size_of vals refs seen

Where:

〘[1,2,3]〙



      Block 67 cons_tag [Number 1;
        Block 66 cons_tag [Number 2;
          Block 65 cons_tag [Number 3;
            Block 0 nil_tag []]]]
    

    size_of [〘[1,2,3]〙;〘[1,2,3]〙] refs seen
  

    10 + size_of [〘[1,2,3]〙] refs ({67,66,65} ∪ seen)
  

    10 + size_of [Block 67 ...] refs ({67,66,65} ∪ seen)
  

    10 + 0
  
size_of vals refs seen


    fun yes = (print "yes"; yes)
  

\[\neg\]

💥

🙂

You can prove it!

🙃

It's complicated...


    size_of 〘[2,...,n]〙 refs seen

    <=

    size_of 〘[1,2,..,n]〙 refs seen
  

    size_of [Block 66 cons_tag [Number 2;...]] refs seen

    <=

    size_of [Block 67 cons_tag [Number 1;
               Block 66 cons_tag [Number 2;...]]] refs seen
  
67 ∈ seen

?

67 ∈ seen
67 ∈ seen


      size_of [Block 67 ...] refs seen = 0
    

      size_of [Block 66 ...] refs seen
      <=
      size_of [Block 67 ...] refs seen
    

      size_of [Block 66 ...] refs seen <= 0
    
67 ∉ seen
67 ∉ seen


      size_of [Block 67 ...] ...
        = 3 + size_of [Block 66 ...] refs ({67} ∪ seen)
    
67 ∉ seen


      size_of [Block 67 ...] ...
        = 3 + size_of [Block 66 ...] refs ({67} ∪ seen)
    

      size_of [Block 66 ...] refs seen
      <=
      size_of [Block 66 ...] refs ({67} ∪ seen)
    

      size_of [Block 66 ...] refs seen
      <=
      size_of [Block 66 ...] refs ({67} ∪ seen)
    

      size_of [Block 66 cons_tag [Number 2;
                Block 67 ...]]
              refs seen
      <=
      size_of [Block 66 cons_tag [Number 2;
                 Block 67 ...]]
              refs ({67} ∪ seen)
     

🤦

size_of vals refs seen

A flat reachability-based measure

flat_size_of refs blocks roots

Where:

blocks
67 |-> Block 67 ... 40 |-> Block 40 ... 10 |-> Block 10 ...

The set of all reachable values


    <|s.locals|> ++ <|s.stack|> ++ <|s.global|>
  


    [
     Block 71 some_tag [Number 1; Block 74 ...];
     Number 57;
     Word64 0xF5CA15;
     Word64 0x01368E;
     Word64 0xC81026;
     RefPtr 45;
     Block 75 some_tag [RefPtr 84; ...];
    ]
  

The set of all reachable values addresses


  addr = TStamp num  -- Blocks
       | RStamp num  -- Pointers

  to_addrs [] = ∅
  to_addrs (Block ts _ _::xs) = {TStamp ts} ∪ to_addrs xs
  to_addrs (RefPtr ptr  ::xs) = {RStamp ts} ∪ to_addrs xs
  to_addrs (_           ::xs) =               to_addrs xs

  to_addrs ([
   Block 71 some_tag [Number 1; Block 74 ...];
   Number 57;
   Word64 0xF5CA15;
   Word64 0x01368E;
   Word64 0xC81026;
   RefPtr 45;
   Block 75 some_tag [RefPtr 84; ...];
  ])
  

    {TStamp 71; RStamp 45; TStamp 75}
  

    to_addrs ([
     Block 71 some_tag [Number 1; Block 74 ...];
     Number 57;
     Word64 0xF5CA15;
     Word64 0x01368E;
     Word64 0xC81026;
     RefPtr 45;
     Block 75 some_tag [RefPtr 84; ...];
    ])
    

      TStamp 71 -> TStamp 74
      TStamp 75 -> RStamp 84
      RStamp 45 -> ...
    

The set of all reachable addresses

=


      reachable_addrs refs blocks roots =
        { y | ∃x. x ∈ roots ∧ x ->* y }
    

Now we measure!


  flat_measure (Word64 _) = 3
  flat_measure (Number i) =
     if small_num i
     then 0
     else ... -- big num stuff
  flat_measure _ = 0
 
RStamp 45 TStamp 50 RStamp 14 TStamp 75
-> -> -> ->
[33,Block 43 ...] [Ptr 30,Ptr 94] [53,85,17] [0xFF0D,0xAB0D,0xDD12]
RStamp 45 TStamp 50 RStamp 14 TStamp 75
-> -> -> ->
3 2 6 12

Σ

RStamp 45 TStamp 50 RStamp 14 TStamp 75
-> -> -> ->
3 2 6 12

23


  addrs_measure refs blocks (TStamp ts) =
    case lookup ts blocks of
    | SOME (Block _ _ vs) =>
        1 + LENGTH vs + SUM (MAP flat_measure vs)
    | _ => 0
  addrs_measure refs blocks (RStamp p) =
    ...
 

    flat_size_of refs blocks roots =
      SUM (MAP flat_measure roots) +
        𝚺 (addrs_measure refs blocks)
          (reachable_addrs refs blocks (to_addrs roots))
  

      flat_size_of s.refs s.all_blocks 〘[2,...,n]〙

      <=

      flat_size_of s.refs s.all_blocks 〘[1,2,..,n]〙
    

      a ⊆ b
      ==>
      𝚺 f a <= 𝚺 f b
    

      reachable_addrs refs blocks
        [Block 66 ...]

      ⊆

      reachable_addrs refs blocks
        [Block 67 cons_tag [Number 1;
           Block 66 ...]]

    

      {TStamp 66; ...}

      ⊆

      {TStamp 67;TStamp 66; ...}
    

🎉

flat_size_of refs blocks roots

🍒

flat_size_of refs blocks vals

🤝

size_of vals refs seen