/red/runtime/allocator.reds
Unknown | 773 lines | 668 code | 105 blank | 0 comment | 0 complexity | c6d866d5620d22a9b1225570de222860 MD5 | raw file
1Red/System [ 2 Title: "Red memory allocator" 3 Author: "Nenad Rakocevic" 4 File: %allocator.reds 5 Rights: "Copyright (C) 2011 Nenad Rakocevic. All rights reserved." 6 License: { 7 Distributed under the Boost Software License, Version 1.0. 8 See https://github.com/dockimbel/Red/blob/master/red-system/runtime/BSL-License.txt 9 } 10] 11 12;-- New built-in natives worth adding to Red/System?? 13; get-bit n value 14; set-bit n value 15; clear-bit n value 16; bit-set? n value 17 18;-- cell header bits layout -- 19; 31: mark ;-- mark as referenced for the GC (mark phase) 20; 30: lock ;-- lock slot for active thread access only 21; 29: immutable ;-- mark as read-only (series only) 22; 28: new-line ;-- new-line (LF) marker (before the slot) 23; 27: big ;-- indicates a big series (big-frame!) 24; 26: stack ;-- series buffer is allocated on stack (series only) 25; 25: permanent ;-- protected from GC (system-critical series) 26; 25-8: <reserved> 27; 7-0: datatype ID ;-- datatype number 28 29#define _128KB 131072 ; @@ create a dedicated datatype? 30#define _2MB 2097152 31#define _16MB 16777216 32#define nodes-per-frame 5000 33#define node-frame-size [((nodes-per-frame * 2 * size? pointer!) + size? node-frame!)] 34 35#define series-in-use 80000000h ;-- mark a series as used (not collectable by the GC) 36#define flag-series-big 40000000h ;-- 1 = big, 0 = series 37#define flag-ins-head 10000000h ;-- optimize for head insertions 38#define flag-ins-tail 20000000h ;-- optimize for tail insertions 39#define flag-ins-both 30000000h ;-- optimize for both head & tail insertions 40#define s-size-mask 00FFFFFFh ;-- mask for 24-bit size field 41 42int-array!: alias struct! [ptr [int-ptr!]] 43 44cell!: alias struct! [ 45 header [integer!] ;-- cell's header flags 46 data1 [integer!] ;-- placeholders to make a 128-bit cell 47 data2 [integer!] 48 data3 [integer!] 49] 50 51series-buffer!: alias struct! [ 52 size [integer!] ;-- bitfield (see below) 53 node [int-ptr!] ;-- point back to referring node 54 head [integer!] ;-- series buffer head index 55 tail [integer!] ;-- series buffer tail index 56] 57;; size bitfield: 58;; 31: used (1 = used, 0 = free) 59;; 30: type (always 0 for series-buffer!) 60;; 29-28: insert-opt (2 = head, 1 = tail, 0 = both) 61;; 27-24: reserved 62;; 23-0: size of allocated buffer 63 64series-frame!: alias struct! [ ;-- series frame header 65 next [series-frame!] ;-- next frame or null 66 prev [series-frame!] ;-- previous frame or null 67 heap [series-buffer!] ;-- point to allocatable region 68 tail [byte-ptr!] ;-- point to last byte in allocatable region 69] 70 71node-frame!: alias struct! [ ;-- node frame header 72 next [node-frame!] ;-- next frame or null 73 prev [node-frame!] ;-- previous frame or null 74 nodes [integer!] ;-- number of nodes 75 bottom [int-ptr!] ;-- bottom of stack (last entry, fixed) 76 top [int-ptr!] ;-- top of stack (first entry, moving) 77] 78 79big-frame!: alias struct! [ ;-- big frame header (for >= 2MB series) 80 flags [integer!] ;-- bit 30: 1 (type = big) 81 next [big-frame!] ;-- next frame or null 82 size [integer!] ;-- size (up to 4GB - size? header) 83 padding [integer!] ;-- make this header same size as series-buffer! header 84] 85 86memory: declare struct! [ ; TBD: instanciate this structure per OS thread 87 total [integer!] ;-- total memory size allocated (in bytes) 88 n-head [node-frame!] ;-- head of node frames list 89 n-active [node-frame!] ;-- actively used node frame 90 n-tail [node-frame!] ;-- tail of node frames list 91 s-head [series-frame!] ;-- head of series frames list 92 s-active [series-frame!] ;-- actively used series frame 93 s-tail [series-frame!] ;-- tail of series frames list 94 s-start [integer!] ;-- start size for new series frame (1) 95 s-size [integer!] ;-- current size for new series frame (1) 96 s-max [integer!] ;-- max size for new series frames (1) 97 b-head [big-frame!] ;-- head of big frames list 98] 99 100memory/total: 0 101memory/s-start: _128KB 102memory/s-max: _2MB 103memory/s-size: memory/s-start 104;; (1) Series frames size will grow from 128KB up to 2MB (arbitrary selected). This 105;; range will need fine-tuning with real Red apps. This growing size, with low starting value 106;; will allow small apps to not consume much memory while avoiding to penalize big apps. 107 108 109;------------------------------------------- 110;-- Allocate paged virtual memory region 111;------------------------------------------- 112allocate-virtual: func [ 113 size [integer!] ;-- allocated size in bytes (page size multiple) 114 exec? [logic!] ;-- TRUE => executable region 115 return: [int-ptr!] ;-- allocated memory region pointer 116 /local ptr 117][ 118 size: round-to size + 4 OS-page-size ;-- account for header (one word) 119 memory/total: memory/total + size 120 ptr: OS-allocate-virtual size exec? 121 ptr/value: size ;-- store size in header 122 ptr + 1 ;-- return pointer after header 123] 124 125;------------------------------------------- 126;-- Free paged virtual memory region from OS 127;------------------------------------------- 128free-virtual: func [ 129 ptr [int-ptr!] ;-- address of memory region to release 130][ 131 ptr: ptr - 1 ;-- return back to header 132 memory/total: memory/total - ptr/value 133 OS-free-virtual ptr 134] 135 136;------------------------------------------- 137;-- Free all frames (part of Red's global exit handler) 138;------------------------------------------- 139free-all: func [ 140 /local n-frame s-frame b-frame n-next s-next b-next 141][ 142 n-frame: memory/n-head 143 while [n-frame <> null][ 144 n-next: n-frame/next 145 free-virtual as int-ptr! n-frame 146 n-frame: n-next 147 ] 148 149 s-frame: memory/s-head 150 while [s-frame <> null][ 151 s-next: s-frame/next 152 free-virtual as int-ptr! s-frame 153 s-frame: s-next 154 ] 155 156 b-frame: memory/b-head 157 while [b-frame <> null][ 158 b-next: b-frame/next 159 free-virtual as int-ptr! b-frame 160 b-frame: b-next 161 ] 162] 163 164;------------------------------------------- 165;-- Format the node frame stack by filling it with pointers to all nodes 166;------------------------------------------- 167format-node-stack: func [ 168 frame [node-frame!] ;-- node frame to format 169 /local node ptr 170][ 171 ptr: frame/bottom ;-- point to bottom of stack 172 node: ptr + frame/nodes ;-- first free node address 173 until [ 174 ptr/value: as-integer node ;-- store free node address on stack 175 node: node + 1 176 ptr: ptr + 1 177 ptr > frame/top ;-- until the stack is filled up 178 ] 179] 180 181;------------------------------------------- 182;-- Allocate a node frame buffer and initialize it 183;------------------------------------------- 184alloc-node-frame: func [ 185 size [integer!] ;-- nb of nodes 186 return: [node-frame!] ;-- newly initialized frame 187 /local sz frame 188][ 189 assert positive? size 190 sz: size * 2 * (size? pointer!) + (size? node-frame!) ;-- total required size for a node frame 191 frame: as node-frame! allocate-virtual sz no ;-- R/W only 192 193 frame/prev: null 194 frame/next: null 195 frame/nodes: size 196 197 frame/bottom: as int-ptr! (as byte-ptr! frame) + size? node-frame! 198 frame/top: frame/bottom + size - 1 ;-- point to the top element 199 200 either null? memory/n-head [ 201 memory/n-head: frame ;-- first item in the list 202 memory/n-tail: frame 203 memory/n-active: frame 204 ][ 205 memory/n-tail/next: frame ;-- append new item at tail of the list 206 frame/prev: memory/n-tail ;-- link back to previous tail 207 memory/n-tail: frame ;-- now tail is the new item 208 ] 209 210 format-node-stack frame ;-- prepare the node frame for use 211 frame 212] 213 214;------------------------------------------- 215;-- Release a node frame buffer 216;------------------------------------------- 217free-node-frame: func [ 218 frame [node-frame!] ;-- frame to release 219][ 220 either null? frame/prev [ ;-- if frame = head 221 memory/n-head: frame/next ;-- head now points to next one 222 ][ 223 either null? frame/next [ ;-- if frame = tail 224 memory/n-tail: frame/prev ;-- tail is now at one position back 225 ][ 226 frame/prev/next: frame/next ;-- link preceding frame to next frame 227 frame/next/prev: frame/prev ;-- link back next frame to preceding frame 228 ] 229 ] 230 if memory/n-active = frame [ 231 memory/n-active: memory/n-tail ;-- reset active frame to last one @@ 232 ] 233 234 assert not all [ ;-- ensure that list is not empty 235 null? memory/n-head 236 null? memory/n-tail 237 ] 238 239 free-virtual as int-ptr! frame ;-- release the memory to the OS 240] 241 242;------------------------------------------- 243;-- Obtain a free node from a node frame 244;------------------------------------------- 245alloc-node: func [ 246 return: [int-ptr!] ;-- return a free node pointer 247 /local frame node 248][ 249 frame: memory/n-active ;-- take node from active node frame 250 node: as int-ptr! frame/top/value ;-- pop free node address from stack 251 frame/top: frame/top - 1 252 253 if frame/top = frame/bottom [ 254 ; TBD: trigger a "light" GC pass from here and update memory/n-active 255 frame: alloc-node-frame nodes-per-frame ;-- allocate a new frame 256 memory/n-active: frame ;@@ to be removed once GC implemented 257 node: as int-ptr! frame/top/value ;-- pop free node address from stack 258 frame/top: frame/top - 1 259 ] 260 node 261] 262 263;------------------------------------------- 264;-- Release a used node 265;------------------------------------------- 266free-node: func [ 267 node [int-ptr!] ;-- node to release 268 /local frame offset 269][ 270 assert not null? node 271 272 frame: memory/n-active 273 offset: as-integer node - frame 274 275 unless all [ 276 positive? offset ;-- check if node address is part of active frame 277 offset < node-frame-size 278 ][ ;@@ following code not be needed if freed only by the GC... 279 frame: memory/n-head ;-- search for right frame from head of the list 280 while [ ; @@ could be optimized by searching backward/forward from active frame 281 offset: as-integer node - frame 282 not all [ ;-- test if node address is part of that frame 283 positive? offset 284 offset < node-frame-size ; @@ check upper bound case 285 ] 286 ][ 287 frame: frame/next 288 assert frame <> null ;-- should found the right one before the list end 289 ] 290 ] 291 292 frame/top: frame/top + 1 ;-- free node by pushing its address on stack 293 frame/top/value: as-integer node 294 295 assert frame/top < (frame/bottom + frame/nodes) ;-- top should not overflow 296] 297 298;------------------------------------------- 299;-- Allocate a series frame buffer 300;------------------------------------------- 301alloc-series-frame: func [ 302 return: [series-frame!] ;-- newly initialized frame 303 /local size frame 304][ 305 size: memory/s-size 306 if size < memory/s-max [memory/s-size: size * 2] 307 308 size: size + size? series-frame! ;-- total required size for a series frame 309 frame: as series-frame! allocate-virtual size no ;-- R/W only 310 311 either null? memory/s-head [ 312 memory/s-head: frame ;-- first item in the list 313 memory/s-tail: frame 314 memory/s-active: frame 315 frame/prev: null 316 ][ 317 memory/s-tail/next: frame ;-- append new item at tail of the list 318 frame/prev: memory/s-tail ;-- link back to previous tail 319 memory/s-tail: frame ;-- now tail is the new item 320 ] 321 322 frame/next: null 323 frame/heap: as series-buffer! (as byte-ptr! frame) + size? series-frame! 324 frame/tail: (as byte-ptr! frame) + size ;-- point to last byte in frame 325 frame 326] 327 328;------------------------------------------- 329;-- Release a series frame buffer 330;------------------------------------------- 331free-series-frame: func [ 332 frame [series-frame!] ;-- frame to release 333][ 334 either null? frame/prev [ ;-- if frame = head 335 memory/s-head: frame/next ;-- head now points to next one 336 ][ 337 either null? frame/next [ ;-- if frame = tail 338 memory/s-tail: frame/prev ;-- tail is now at one position back 339 ][ 340 frame/prev/next: frame/next ;-- link preceding frame to next frame 341 frame/next/prev: frame/prev ;-- link back next frame to preceding frame 342 ] 343 ] 344 if memory/s-active = frame [ 345 memory/s-active: memory/s-tail ;-- reset active frame to last one @@ 346 ] 347 348 assert not all [ ;-- ensure that list is not empty 349 null? memory/s-head 350 null? memory/s-tail 351 ] 352 353 free-virtual as int-ptr! frame ;-- release the memory to the OS 354] 355 356;------------------------------------------- 357;-- Update node back-reference from moved series buffers 358;------------------------------------------- 359update-series-nodes: func [ 360 series [series-buffer!] ;-- start of series region with nodes to re-sync 361][ 362 until [ 363 ;-- update the node pointer to the new series address 364 series/node/value: as-integer (as byte-ptr! series) + size? series-buffer! 365 366 ;-- advance to the next series buffer 367 series: as series-buffer! (as byte-ptr! series) + (series/size and s-size-mask) 368 369 ;-- exit when a freed series is met (<=> end of region) 370 zero? (series/size and series-in-use) 371 ] 372] 373 374;------------------------------------------- 375;-- Compact a series frame by moving down in-use series buffer regions 376;------------------------------------------- 377#define SM1_INIT 1 ;-- enter the state machine 378#define SM1_HOLE 2 ;-- begin of contiguous region of freed buffers (hole) 379#define SM1_HOLE_END 3 ;-- end of freed buffers region 380#define SM1_USED 4 ;-- begin of contiguous region of buffers in use 381#define SM1_USED_END 5 ;-- end of used buffers region 382 383compact-series-frame: func [ 384 frame [series-frame!] ;-- series frame to compact 385 /local heap series state 386 free? [logic!] src [byte-ptr!] dst [byte-ptr!] 387][ 388 series: as series-buffer! (as byte-ptr! frame) + size? series-frame! ;-- point to first series buffer 389 free?: zero? (series/size and series-in-use) ;-- true: series is not used 390 heap: frame/heap 391 392 src: null ;-- src will point to start of buffer region to move down 393 dst: null ;-- dst will point to start of free region 394 state: SM1_INIT 395 396 until [ 397 if all [state = SM1_INIT free?][ 398 dst: as byte-ptr! series ;-- start of "hole" region 399 state: SM1_HOLE 400 ] 401 if all [state = SM1_HOLE not free?][ ;-- search for first used series (<=> end of hole) 402 state: SM1_HOLE_END 403 ] 404 if state = SM1_HOLE_END [ 405 src: as byte-ptr! series ;-- start of new "alive" region 406 state: SM1_USED 407 ] 408 ;-- point to next series buffer 409 series: as series-buffer! (as byte-ptr! series) + (series/size and s-size-mask) 410 free?: zero? (series/size and series-in-use) ;-- true: series is not used 411 412 if all [state = SM1_USED any [free? series >= heap]][ ;-- handle both normal and "exit" states 413 state: SM1_USED_END 414 ] 415 if state = SM1_USED_END [ 416 assert dst < src ;-- regions are moved down in memory 417 assert src < as byte-ptr! series ;-- src should point at least at series - series/size 418 copy-memory dst src as-integer series - src 419 update-series-nodes as series-buffer! dst 420 dst: dst + (as-integer series - src) ;-- points after moved region (ready for next move) 421 state: SM1_HOLE 422 ] 423 series >= heap ;-- exit state machine 424 ] 425 426 unless null? dst [ ;-- no compaction occurred, all series were in use 427 frame/heap: as series-buffer! dst ;-- set new heap after last moved region 428 ] 429] 430 431;------------------------------------------- 432;-- Allocate a series from the active series frame, return the series 433;------------------------------------------- 434alloc-series-buffer: func [ 435 size [integer!] ;-- size in bytes 436 return: [series-buffer!] ;-- return the new series buffer 437 /local series frame sz 438][ 439 assert positive? size ;-- size is not zero or negative 440 size: round-to size 16 ;-- size is a multiple of 16 (one cell! size) 441 442 frame: memory/s-active 443 sz: size + size? series-buffer! ;-- add series header size 444 445 ;-- size should not be greater than the frame capacity 446 assert sz < as-integer (frame/tail - ((as byte-ptr! frame) + size? series-frame!)) 447 448 series: frame/heap 449 if ((as byte-ptr! series) + sz) >= frame/tail [ 450 ; TBD: trigger a GC pass from here and update memory/s-active 451 frame: alloc-series-frame 452 memory/s-active: frame ;@@ to be removed once GC implemented 453 series: frame/heap 454 ] 455 456 assert sz < _16MB ;-- max series size allowed in a series frame 457 458 frame/heap: as series-buffer! (as byte-ptr! frame/heap) + sz 459 460 series/size: sz 461 or series-in-use ;-- mark series as in-use 462 or flag-ins-both ;-- optimize for both head & tail insertions (default) 463 and not flag-series-big ;-- set type bit to 0 (= series) 464 465 series/head: size / 2 ;-- position empty series at middle of buffer 466 series/tail: series/head 467 series 468] 469 470;------------------------------------------- 471;-- Allocate a node and a series from the active series frame, return the node 472;------------------------------------------- 473alloc-series: func [ 474 size [integer!] ;-- size in multiple of 16 bytes (cell! size) 475 return: [int-ptr!] ;-- return a new node pointer (pointing to the newly allocated series buffer) 476 /local series node 477][ 478 series: alloc-series-buffer size 479 node: alloc-node ;-- get a new node 480 series/node: node ;-- link back series to node 481 ;-- make node points to first usable byte of series buffer 482 node/value: as-integer (as byte-ptr! series) + size? series-buffer! 483 node ;-- return the node pointer 484] 485 486;------------------------------------------- 487;-- Release a series 488;------------------------------------------- 489free-series: func [ 490 frame [series-frame!] ;-- frame containing the series (should be provided by the GC) 491 node [int-ptr!] ;-- series' node pointer 492 /local series 493][ 494 assert not null? frame 495 assert not null? node 496 497 series: as series-buffer! ((as byte-ptr! node/value) - size? series-buffer!) ;-- point back to series header 498 499 assert not zero? (series/size and not series-in-use) ;-- ensure that 'used bit is set 500 series/size: series/size xor series-in-use ;-- clear 'used bit (enough to free the series) 501 502 if frame/heap = as series-buffer! ( ;-- test if series is on top of heap 503 (as byte-ptr! node/value) + (series/size and s-size-mask) 504 ) [ 505 frame/heap = series ;-- cheap collecting of last allocated series 506 ] 507 508 free-node node 509] 510 511;------------------------------------------- 512;-- Expand a series to a new size 513;------------------------------------------- 514expand-series: func [ 515 series [series-buffer!] ;-- series to expand 516 new-sz [integer!] ;-- new size 517 return: [series-buffer!] ;-- return new series with new size 518 /local new 519][ 520 assert not null? series 521 assert new-sz > (series/size and s-size-mask) ;-- ensure requested size is bigger than current one 522 523 new: alloc-series-buffer new-sz 524 series/node/value: as-integer new ;-- link node to new series buffer 525 526 ;TBD: honor flag-ins-head and flag-ins-tail when copying! 527 528 copy-memory ;-- copy old series in new buffer (including header) 529 as byte-ptr! new 530 as byte-ptr! series 531 series/size + size? series-buffer! 532 533 assert not zero? (series/size and not series-in-use) ;-- ensure that 'used bit is set 534 series/size: series/size xor series-in-use ;-- clear 'used bit (enough to free the series) 535 new 536] 537 538;------------------------------------------- 539;-- Shrink a series to a smaller size (not needed for now) 540;------------------------------------------- 541;shrink-series: func [ 542; series [series-buffer!] 543; return: [series-buffer!] 544;][ 545; 546;] 547 548 549;------------------------------------------- 550;-- Allocate a big series 551;------------------------------------------- 552alloc-big: func [ 553 size [integer!] ;-- buffer size to allocate (in bytes) 554 return: [byte-ptr!] ;-- return allocated buffer pointer 555 /local sz frame frm 556][ 557 assert positive? size 558 assert size >= _2MB ;-- should be bigger than a series frame 559 560 sz: size + size? big-frame! ;-- total required size for a big frame 561 frame: as big-frame! allocate-virtual sz no ;-- R/W only 562 563 frame/next: null 564 frame/size: size 565 566 either null? memory/b-head [ 567 memory/b-head: frame ;-- first item in the list 568 ][ 569 frm: memory/b-head ;-- search for tail of list (@@ might want to save it?) 570 until [frm: frm/next null? frm/next] 571 assert not null? frm 572 573 frm/next: frame ;-- append new item at tail of the list 574 ] 575 576 (as byte-ptr! frame) + size? big-frame! ;-- return a pointer to the requested buffer 577] 578 579;------------------------------------------- 580;-- Release a big series 581;------------------------------------------- 582free-big: func [ 583 buffer [byte-ptr!] ;-- big buffer to release 584 /local frame frm 585][ 586 assert not null? buffer 587 588 frame: as big-frame! (buffer - size? big-frame!) ;-- point to frame header 589 590 either frame = memory/b-head [ 591 memory/b-head: null 592 ][ 593 frm: memory/b-head ;-- search for frame position in list 594 while [frm/next <> frame][ ;-- frm should point to one item behind frame on exit 595 frm: frm/next 596 assert not null? frm ;-- ensure tail of list is not passed 597 ] 598 frm/next: frame/next ;-- remove frame from list 599 ] 600 601 free-virtual as int-ptr! frame ;-- release the memory to the OS 602] 603 604 605;=========================================== 606;== Debugging functions 607;=========================================== 608 609#if debug? = yes [ 610 611 ;------------------------------------------- 612 ;-- Print usage stats about a given frame 613 ;------------------------------------------- 614 frame-stats: func [ 615 free [integer!] 616 used [integer!] 617 total [integer!] 618 /local percent 619 ][ 620 assert free + used = total 621 percent: 100 * used / total 622 if all [not zero? used zero? percent][percent: 1] 623 624 print [ 625 "used = " used "/" total " (" percent "%), " 626 "free = " free "/" total " (" 100 - percent "%)" lf 627 ] 628 ] 629 630 631 ;------------------------------------------- 632 ;-- List series buffer allocated in a given series frame 633 ;------------------------------------------- 634 list-series-buffers: func [ 635 frame [series-frame!] 636 /local series alt? size block count 637 ][ 638 count: 1 639 series: as series-buffer! (as byte-ptr! frame) + size? series-frame! 640 until [ 641 print [ 642 " - series #" count 643 ": size = " (series/size and s-size-mask) - size? series-buffer! 644 ", offset pos = " series/head ", tail pos = " series/tail 645 " " 646 ] 647 if series/size and flag-ins-head <> 0 [print "H"] 648 if series/size and flag-ins-tail <> 0 [print "T"] 649 print lf 650 count: count + 1 651 652 series: as series-buffer! (as byte-ptr! series) + (series/size and s-size-mask) 653 series >= frame/heap 654 ] 655 assert series = frame/heap 656 ] 657 658 ;------------------------------------------- 659 ;-- Displays total frames count 660 ;------------------------------------------- 661 print-frames-count: func [count [integer!] /local s][ 662 s: either count > 1 ["s^/"][newline] 663 print ["^/ " count " frame" s lf] 664 ] 665 666 ;------------------------------------------- 667 ;-- Dump memory statistics on screen 668 ;------------------------------------------- 669 memory-stats: func [ 670 verbose [integer!] ;-- stat verbosity level (1, 2 or 3) 671 /local count n-frame s-frame b-frame free-nodes base 672 ][ 673 assert all [1 <= verbose verbose <= 3] 674 675 print [lf "====== Red Memory Stats ======" lf] 676 677 ;-- Node frames stats -- 678 count: 0 679 n-frame: memory/n-head 680 681 print [lf "-- Node frames --" lf] 682 while [n-frame <> null][ 683 if verbose >= 2 [ 684 print ["#" count + 1 ": "] 685 free-nodes: (as-integer (n-frame/top - n-frame/bottom) + 1) / 4 686 frame-stats 687 free-nodes 688 n-frame/nodes - free-nodes 689 n-frame/nodes 690 ] 691 count: count + 1 692 n-frame: n-frame/next 693 ] 694 print-frames-count count 695 696 ;-- Series frames stats -- 697 count: 0 698 s-frame: memory/s-head 699 700 print ["-- Series frames --" lf] 701 while [s-frame <> null][ 702 if verbose >= 2 [ 703 print ["#" count + 1 ": "] 704 base: (as byte-ptr! s-frame) + size? series-frame! 705 frame-stats 706 as-integer s-frame/tail - as byte-ptr! s-frame/heap 707 as-integer (as byte-ptr! s-frame/heap) - base 708 as-integer s-frame/tail - base 709 if verbose >= 3 [ 710 list-series-buffers s-frame 711 ] 712 ] 713 count: count + 1 714 s-frame: s-frame/next 715 ] 716 print-frames-count count 717 718 ;-- Big frames stats -- 719 count: 0 720 b-frame: memory/b-head 721 722 print ["-- Big frames --" lf] 723 while [b-frame <> null][ 724 if verbose >= 2 [ 725 print ["#" count + 1 ": "] 726 prin-int b-frame/size 727 ] 728 count: count + 1 729 b-frame: b-frame/next 730 ] 731 print-frames-count count 732 733 print [ 734 "Total memory used: " memory/total " bytes" lf 735 "==============================" lf 736 ] 737 ] 738 739 ;------------------------------------------- 740 ;-- Dump memory layout of a given series frame 741 ;------------------------------------------- 742 dump-series-frame: func [ 743 frame [series-frame!] 744 /local series alt? size block 745 ][ 746 series: as series-buffer! (as byte-ptr! frame) + size? series-frame! 747 748 print [lf "=== Series layout for frame: <" frame "h>" lf] 749 750 alt?: no 751 until [ 752 block: either zero? (series/size and series-in-use) [ 753 "." 754 ][ 755 alt?: not alt? 756 either alt? ["x"]["o"] 757 ] 758 759 size: ((series/size and s-size-mask) - size? series-buffer!) / 16 760 until [ 761 print block 762 size: size - 1 763 zero? size 764 ] 765 766 series: as series-buffer! (as byte-ptr! series) + (series/size and s-size-mask) 767 series >= frame/heap 768 ] 769 assert series = frame/heap 770 print lf 771 ] 772 773]