Skip to content

Commit

Permalink
Bugfix: Performance - Memory climb on startup (#432)
Browse files Browse the repository at this point in the history
* Remove garbage collector tuning code

* Factor to include onIdle function

* Print idle message in example app

* Formatting
  • Loading branch information
bryphe authored Apr 3, 2019
1 parent 3eb5ae0 commit 339f456
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 84 deletions.
3 changes: 2 additions & 1 deletion examples/Examples.re
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,5 @@ let init = app => {
UI.start(win, render);
};

App.startWithState(state, reducer, init);
let onIdle = () => print_endline("Example: idle callback triggered");
App.startWithState(~onIdle, state, reducer, init);
108 changes: 51 additions & 57 deletions src/Core/App.re
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ open Reglfw;

type reducer('s, 'a) = ('s, 'a) => 's;

type idleState =
| Running
| Idle;
type idleFunc = unit => unit;
let noop = () => ();

type t('s, 'a) = {
reducer: reducer('s, 'a),
mutable state: 's,
mutable windows: list(Window.t),
mutable needsRender: bool,
mutable idleState,
mutable idleCount: int,
onIdle: idleFunc,
};

let framesToIdle = 10;

/* If no state is specified, just use unit! */
let defaultState = ();

Expand All @@ -22,8 +24,6 @@ let defaultReducer: reducer(unit, unit) = (_s, _a) => ();

type appInitFunc('s, 'a) = t('s, 'a) => unit;

type startFunc('s, 'a) = ('s, reducer('s, 'a), appInitFunc('s, 'a)) => unit;

let getWindows = (app: t('s, 'a)) => app.windows;

let getState = (app: t('s, 'a)) => app.state;
Expand Down Expand Up @@ -62,61 +62,55 @@ let _checkAndCloseWindows = (app: t('s, 'a)) => {
app.windows = windowsToKeep;
};

let startWithState: startFunc('s, 'a) =
(
initialState: 's,
reducer: reducer('s, 'a),
initFunc: appInitFunc('s, 'a),
) => {
let appInstance: t('s, 'a) = {
reducer,
state: initialState,
windows: [],
needsRender: true,
idleState: Running,
};
let startWithState =
(
~onIdle=noop,
initialState: 's,
reducer: reducer('s, 'a),
initFunc: appInitFunc('s, 'a),
) => {
let appInstance: t('s, 'a) = {
reducer,
state: initialState,
windows: [],
needsRender: true,
idleCount: 0,
onIdle,
};

GarbageCollector.tune();

let _ = Glfw.glfwInit();
let _ = initFunc(appInstance);

let appLoop = (_t: float) => {
Glfw.glfwPollEvents();
Tick.Default.pump();

_checkAndCloseWindows(appInstance);

if (appInstance.needsRender || _anyWindowsDirty(appInstance)) {
Performance.bench("renderWindows", () => {
List.iter(w => Window.render(w), getWindows(appInstance));
appInstance.needsRender = false;
});
/* We're taking path 2 of the garbage collector route to nirvana:
* https://blogs.msdn.microsoft.com/shawnhar/2007/07/02/twin-paths-to-garbage-collector-nirvana/
*/
appInstance.idleState = Running;
Performance.bench("gc: minor", () => GarbageCollector.minor());
} else if (appInstance.idleState == Running) {
/* If the we're transitioning from Running to Idle, this is the
* perfect time to do a full memory collection, so that
* we're in a clear memory state, so we're ready to go on the next
* tick
*/
Performance.bench("gc: full", () => GarbageCollector.full());
appInstance.idleState = Idle;
} else {
Environment.sleep(Milliseconds(1.));
};
let _ = Glfw.glfwInit();
let _ = initFunc(appInstance);

let appLoop = (_t: float) => {
Glfw.glfwPollEvents();
Tick.Default.pump();

if (Environment.isNative) {
Thread.yield();
_checkAndCloseWindows(appInstance);

if (appInstance.needsRender || _anyWindowsDirty(appInstance)) {
Performance.bench("renderWindows", () => {
List.iter(w => Window.render(w), getWindows(appInstance));
appInstance.needsRender = false;
appInstance.idleCount = 0;
});
} else {
appInstance.idleCount = appInstance.idleCount + 1;

if (appInstance.idleCount === framesToIdle) {
appInstance.onIdle();
};
List.length(getWindows(appInstance)) == 0;

Environment.sleep(Milliseconds(1.));
};

Glfw.glfwRenderLoop(appLoop);
if (Environment.isNative) {
Thread.yield();
};
List.length(getWindows(appInstance)) == 0;
};

let start = (initFunc: appInitFunc(unit, unit)) =>
startWithState(defaultState, defaultReducer, initFunc);
Glfw.glfwRenderLoop(appLoop);
};

let start = (~onIdle=noop, initFunc: appInitFunc(unit, unit)) =>
startWithState(~onIdle, defaultState, defaultReducer, initFunc);
26 changes: 0 additions & 26 deletions src/Core/GarbageCollector.re
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,6 @@
* Revery app model.
*/

/* Increase minor heap size: 256kb -> 8MB */
/*
* Some more info on why this is helpful:
* https://md.ekstrandom.net/blog/2010/06/ocaml-memory-tuning
*/
let minorHeapSize = 8 * 1024 * 1024;

/*
* Amortize major collections across frames
*/
let defaultSliceSize = minorHeapSize / 60;

let tune = () =>
/* Gc tuning is only applicable in the native space */
if (Environment.isNative) {
let settings = Gc.get();

Gc.set({...settings, minor_heap_size: minorHeapSize});
};

let minor = () =>
if (Environment.isNative) {
let _ = Gc.major_slice(defaultSliceSize);
();
};

let full = () =>
if (Environment.isNative) {
Gc.full_major();
Expand Down

0 comments on commit 339f456

Please sign in to comment.