     dup redundant-root-node? [ children>> values first trim-root ] when ;
 
 : (top-down) ( samples -- tree )
-    collect-contexts [
+    collect-threads [
         [ sum-times ] [ collect-tops ] bi <profile-node> trim-root
     ] assoc-map ;
 
 : top-down ( -- tree )
     get-raw-profile-data (top-down) ;
+
+:: collect-flat ( samples -- flat )
+    IH{ } clone :> per-word-samples
+    samples [| sample |
+        sample sample-callstack unique keys [
+            per-word-samples [ { 0 0 0 0 } or sample 4 head-slice v+ ] change-at
+        ] each
+    ] each
+    per-word-samples [ f <profile-node> ] assoc-map ;
+
+: (flat) ( samples -- flat )
+    collect-threads [
+        [ sum-times ] [ collect-flat ] bi <profile-node>
+    ] assoc-map ;
+
+: flat ( -- tree )
+    get-raw-profile-data (flat) ;
 
 : depth. ( depth -- )
     [ "  " write ] times ;

    total-time gc-time foreign-time foreign-thread-time children ;

: <profile-node> ( times children -- node )
    [ first4 ] dip profile-node boa ;

: leaf-callstack? ( callstack -- ? )
    [ ignore-words member? ] all? ;

: sum-times ( samples -- times )
    { 0 0 0 0 } [ 4 head-slice v+ ] reduce [ samples>time ] map! ;

:: (collect-subtrees) ( samples child-quot -- children )
    samples [ sample-callstack leaf-callstack? not ] filter

: gc-sample-count ( sample -- count ) second ;
: foreign-sample-count ( sample -- count ) third ;
: foreign-thread-sample-count ( sample -- count ) fourth ;
: sample-thread ( sample -- alien ) 4 swap nth ;
: sample-callstack ( sample -- array ) 5 swap nth ;

: samples>time ( samples -- time )
