/red/runtime/allocator.reds

http://github.com/dockimbel/Red · Redscript · 773 lines · 668 code · 105 blank · 0 comment · 55 complexity · c6d866d5620d22a9b1225570de222860 MD5 · raw file

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