Code tuning and differences between simulator and real devices


I have made a short demo (https://drive.google.com/open?id=0By...wLVp5SmdNSTg)* for the forum members today to check some graphic functions - in the simulator the program works relatively smooth but on my FR935 it seems to be significant slower. It's clear for me that the simulator is a simulator, not an emulator and it would be impossible to get a "perfect" simulation for all (!) different devices...

...but are there already some experiences about some bigger shifts between simulator and some devices? I would mention fonts here which look different on my watch and which sometimes mean working hard to find the best x|y values for the text coordinates (that is quite interesting, because the font files of the SDK need quite a lot of disk space)

...and what about speed? Does anyone give some hints what should work fine or where things won't work as expected? Which functions are relatively slow, where the compiler makes optimized code and where not? (e.g. should I prefer 'i=i+i/2' or 'i=i+i>>1'... [TEXT NOT COMPLETE]

*) play with 'Start', 'Up' and 'Down' - to quit the game before you have completed all levels, press 'Back' and 'Down'
  • Thanks for all your comments.

    You could use use Sys.getTime() and check the time it takes to run each variation a 1000 times or something and see the difference, or I know Travis has demonstrated how to show the byte code generated for each line which also might give you some info. (I think it's an option to the compiler) But step back a bit - what is it you're trying to correct? Is it just that you want to make the code as fast as possible, or is it a specific problem?


    Can't edit the text, so maybe as a screenshot:
    https://drive.google.com/open?id=0By...G9iYTFVczlqdTQ

    Next try, the links to the programs:

    Datafield:
    https://drive.google.com/open?id=0By5sHvX5XO7_YXZRZ2ZFNEoxYUU
    https://drive.google.com/open?id=0By5sHvX5XO7_NG1BdkZNS3U2TW8

    Widget:
    https://drive.google.com/open?id=0By5sHvX5XO7_bV9vUEtRS1RVMGs
    https://drive.google.com/open?id=0By5sHvX5XO7_M1NwLVp5SmdNSTg

  • AFAICT, you're prematurely optimizing. There is absolutely no way that the difference between an integer divide and a integer shift will be something that a user will notice, even if you are doing thousands of them a second. If you want to save battery life, don't redraw the screen unless you really need to. Don't turn on the backlight unless you really need to.

    If you are really worried about runtime performance, there are a few things to keep in mind.

    • Access function parameters or local variables if at all possible.
    • Accessing a class variable is much slower than accessing a local.
    • Accessing a global variable is much slower than accessing a class variable (unless you explicitly qualify the reference with $).
    • Doing arithmetic with Lang.Long or Lang.Double is much more expensive than with Lang.Number or Lang.Float.
    • Using Lang.Number and Lang.Float will use up less memory and will not count against the available object count.


    I'd suggest you just write your application. Once you've got it all working, then spend some time to tune it. As Brian.ConnectIQ said above, the VM is going to be the biggest bottleneck. I'll post some code that will show performance numbers.

    As Jim mentioned, you can look at the generated bytecode by compiling with -g. It isn't really that useful for this type of thing.

    Travis
  • This code shows the number of operations per millisecond for the following
    • x = x + x / 2
    • x = x + x >> 1
    • x += x / 2
    • x += x >> 1
    It does so for both Number and Long. It is currently configured to show the results for local variables.

    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class MyView extends Ui.View
    {
    hidden static const _tests = [
    :testNumberI_Plus_I_Div_2,
    :testLongI_Plus_I_Div_2,
    :testNumberI_Plus_I_RShift_1,
    :testLongI_Plus_I_RShift_1,
    :testNumberI_PlusEqual_I_Div_2,
    :testLongI_PlusEqual_I_Div_2,
    :testNumberI_PlusEqual_I_RShift_1,
    :testLongI_PlusEqual_I_RShift_1
    ];

    hidden static const _rows = [
    "",
    "A",
    "B",
    "C",
    "D"
    ];

    hidden static const _cols = [
    "",
    "N",
    "L"
    ];

    hidden var _mode;
    hidden var _operations;
    hidden var _milliseconds;

    hidden const _iterations = 1000;
    hidden const _min_milliseconds = 500;

    function initialize() {
    View.initialize();

    _mode = 0;
    _operations = new [ _tests.size() ];
    _milliseconds = new [ _tests.size() ];

    for (var i = 0; i < _tests.size(); ++i) {
    _operations= 0;
    _milliseconds= 0;
    }
    }

    function testNumberI_Plus_I_Div_2() {
    return testI_Plus_I_Div_2(1, 1);
    }

    function testNumberI_Plus_I_RShift_1() {
    return testI_Plus_I_Div_2(1, 1);
    }

    function testNumberI_PlusEqual_I_Div_2() {
    return testI_PlusEqual_I_Div_2(1, 1);
    }

    function testNumberI_PlusEqual_I_RShift_1() {
    return testI_PlusEqual_I_RShift_1(1, 1);
    }

    function testLongI_Plus_I_Div_2() {
    return testI_Plus_I_Div_2(1l, 1l);
    }

    function testLongI_Plus_I_RShift_1() {
    return testI_Plus_I_RShift_1(1l, 1l);
    }

    function testLongI_PlusEqual_I_Div_2() {
    return testI_PlusEqual_I_Div_2(1l, 1l);
    }

    function testLongI_PlusEqual_I_RShift_1() {
    return testI_PlusEqual_I_RShift_1(1l, 1l);
    }

    function testI_Plus_I_Div_2(x, y) {
    for (var i = 0; i < _iterations; ++i) {
    x = x + x / 2;
    x = x + x / 2;
    x = x + x / 2;
    x = x + x / 2;
    x = x + x / 2;
    }

    return _iterations * 5;
    }

    function testI_Plus_I_RShift_1(x, y) {
    for (var i = 0; i < _iterations; ++i) {
    x = x + x >> 1;
    x = x + x >> 1;
    x = x + x >> 1;
    x = x + x >> 1;
    x = x + x >> 1;
    }

    return _iterations * 5;
    }

    function testI_PlusEqual_I_Div_2(x, y) {
    for (var i = 0; i < _iterations; ++i) {
    x += x / 2;
    x += x / 2;
    x += x / 2;
    x += x / 2;
    x += x / 2;
    }

    return _iterations * 5;
    }

    function testI_PlusEqual_I_RShift_1(x, y) {
    for (var i = 0; i < _iterations; ++i) {
    x += x >> 1;
    x += x >> 1;
    x += x >> 1;
    x += x >> 1;
    x += x >> 1;
    }

    return _iterations * 5;
    }

    hidden var _timer;

    function onShow() {
    _timer = new Timer.Timer();
    _timer.start(self.method(:onTimer), 1000, true);
    }

    function onHide() {
    _timer.stop();
    _timer = null;
    }

    function onTimer() {
    var mode = _mode % _tests.size();
    _mode += 1;

    var operations = 0;
    var milliseconds = 0;

    var a = Sys.getTimer();

    do {
    operations += method(_tests[mode]).invoke();
    milliseconds = (Sys.getTimer() - a);
    } while (milliseconds < _min_milliseconds);

    _operations[mode] += operations;
    _milliseconds[mode] += milliseconds;

    Ui.requestUpdate();
    }

    hidden function value_for_cell(row, col) {

    if (row == 0) {
    return _cols[col];
    }
    else if (col == 0) {
    return _rows[row];
    }

    row -= 1;
    col -= 1;

    var cell = row * (_cols.size() - 1) + col;

    if (cell < _milliseconds.size() && _milliseconds[cell] != 0) {
    return (_operations[cell] / _milliseconds[cell]).format("%4d");
    }

    return "-";
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.clear();

    var rows = _rows.size();
    var cols = _cols.size();

    var font = Gfx.FONT_XTINY;

    var d = dc.getTextDimensions("0123456789", font);

    var dx = d[0] * 5 / 10.0; // average width of 5 digit
    var dy = d[1] * 1.0; // height of 1 digit

    var x = dc.getWidth() / 2;
    var y = dc.getHeight() / 2;

    x -= (dx * (cols - 1)) / 2;
    y -= (dy * (rows - 0)) / 2;

    for (var row = 0; row < rows; ++row) {
    var yj = y + (row * dy);

    for (var col = 0; col < cols; ++col) {
    var xj = x + (col * dx);

    var value = value_for_cell(row, col);
    if (value != null) {
    dc.drawText(xj, yj, font, value, Gfx.TEXT_JUSTIFY_RIGHT);
    }
    }
    }
    }
    }

    class ZZZApp extends App.AppBase
    {
    function initialize() {
    AppBase.initialize();
    }

    function getInitialView() {
    return [ new MyView() ];
    }
    }
    [/code]

    When I run this on my 735xt, I see about 40 ops/ms with Lang.Number, and 15 ops/ms with Lang.Long.
  • I wrote a quick test based on the above to show the cost associated with reading/writing globals/members/locals. On the 735xt, I get these results..
    • 10op/ms for unqualified global access
    • 19op/ms for qualified global access ($._global)
    • 28op/ms for unqualified class member access
    • 28op/ms for qualified reference to class member access (self._member)
    • 78op/ms for reference to local
    In other words, it is about 8x faster to use a local variable than a global. If you are referencing a global or a class member in a loop, it might make sense to make a local copy, use that for the loop, and then write it back to the global.

    Here is my test code...

    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    var _global = 0;

    class MyView extends Ui.View
    {
    hidden static const _tests = [
    :test_read_global,
    :test_write_global,
    :test_read_global_explicit,
    :test_write_global_explicit,
    :test_read_class,
    :test_write_class,
    :test_read_class_explicit,
    :test_write_class_explicit,
    :test_read_local,
    :test_write_local
    ];

    hidden static const _rows = [
    "",
    "G",
    "GE",
    "C",
    "CE",
    "L"
    ];

    hidden static const _cols = [
    "",
    "R",
    "W"
    ];

    hidden var _mode;
    hidden var _operations;
    hidden var _milliseconds;

    hidden const _iterations = 1000;
    hidden const _min_milliseconds = 500;

    hidden var _member = 0;

    function initialize() {
    View.initialize();

    _mode = 0;
    _operations = new [ _tests.size() ];
    _milliseconds = new [ _tests.size() ];

    for (var i = 0; i < _tests.size(); ++i) {
    _operations= 0;
    _milliseconds= 0;
    }
    }

    function test_read_global_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    }

    return _iterations * 10;
    }

    function test_write_global_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_global(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    }

    return _iterations * 10;
    }

    function test_write_global(x) {
    for (var i = 0; i < _iterations; ++i) {
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_class_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    }

    return _iterations * 10;
    }

    function test_write_class_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_class(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    }

    return _iterations * 10;
    }

    function test_write_class(x) {
    for (var i = 0; i < _iterations; ++i) {
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_local(x) {
    var _local = 0;
    for (var i = 0; i < _iterations; ++i) {
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    }

    return _iterations * 10;
    }

    function test_write_local(x) {
    var _local = 0;
    for (var i = 0; i < _iterations; ++i) {
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    }

    return _iterations * 10;
    }

    hidden var _timer;

    function onShow() {
    _timer = new Timer.Timer();
    _timer.start(self.method(:onTimer), 1000, true);
    }

    function onHide() {
    _timer.stop();
    _timer = null;
    }

    function onTimer() {
    var mode = _mode % _tests.size();
    _mode += 1;

    var operations = 0;
    var milliseconds = 0;

    var a = Sys.getTimer();

    do {
    operations += method(_tests[mode]).invoke(1);
    milliseconds = (Sys.getTimer() - a);
    } while (milliseconds < _min_milliseconds);

    _operations[mode] += operations;
    _milliseconds[mode] += milliseconds;

    Ui.requestUpdate();
    }

    hidden function value_for_cell(row, col) {

    if (row == 0) {
    return _cols[col];
    }
    else if (col == 0) {
    return _rows[row];
    }

    row -= 1;
    col -= 1;

    var cell = row * (_cols.size() - 1) + col;

    if (cell < _milliseconds.size() && _milliseconds[cell] != 0) {
    return (_operations[cell] / _milliseconds[cell]).format("%4d");
    }

    return "-";
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.clear();

    var rows = _rows.size();
    var cols = _cols.size();

    var font = Gfx.FONT_XTINY;

    var d = dc.getTextDimensions("0123456789", font);

    var dx = d[0] * 5 / 10.0; // average width of 5 digit
    var dy = d[1] * 1.0; // height of 1 digit

    var x = dc.getWidth() / 2;
    var y = dc.getHeight() / 2;

    x -= (dx * (cols - 1)) / 2;
    y -= (dy * (rows - 0)) / 2;

    for (var row = 0; row < rows; ++row) {
    var yj = y + (row * dy);

    for (var col = 0; col < cols; ++col) {
    var xj = x + (col * dx);

    var value = value_for_cell(row, col);
    if (value != null) {
    dc.drawText(xj, yj, font, value, Gfx.TEXT_JUSTIFY_RIGHT);
    }
    }
    }
    }
    }

    class ZZZApp extends App.AppBase
    {
    function initialize() {
    AppBase.initialize();
    }

    function getInitialView() {
    return [ new MyView() ];
    }
    }
    [/code]
  • Cool information, thanks!

    Just changed your program a little bit to get rid of the time calling the procedure and doing the loop, also displaying the execution time now.

    Interesting is the difference between simulator and (my only) real device:
    * SDK 2.3.1: 5ms to 16ms (local to global)
    * SDK 2.2.6: 1ms to 9ms
    * FR935: 17 to 130ms


    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    var _global = 0;
    var mode=0;

    class MyView extends Ui.View
    {
    hidden static const _tests = [
    :test_nil,
    :test_nil,
    :test_read_global,
    :test_write_global,
    :test_read_global_explicit,
    :test_write_global_explicit,
    :test_read_class,
    :test_write_class,
    :test_read_class_explicit,
    :test_write_class_explicit,
    :test_read_local,
    :test_write_local
    ];

    hidden static const _rows = [
    "",
    "nil",
    "G",
    "GE",
    "C",
    "CE",
    "L"
    ];

    hidden static const _cols = [
    "",
    "R",
    "W"
    ];

    hidden var _mode;
    hidden var _operations;
    hidden var _milliseconds;

    hidden const _iterations = 1000;
    hidden const _min_milliseconds = 250;

    hidden var _member = 0;

    function initialize() {
    View.initialize();

    _mode = 0;
    _operations = new [ _tests.size() ];
    _milliseconds = new [ _tests.size() ];

    for (var i = 0; i < _tests.size(); ++i) {
    _operations= 0;
    _milliseconds= 0;
    }
    }

    function test_read_global_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    x = $._global + 1;
    }

    return _iterations * 10;
    }

    function test_write_global_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    $._global = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_global(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    x = _global + 1;
    }

    return _iterations * 10;
    }

    function test_write_global(x) {
    for (var i = 0; i < _iterations; ++i) {
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    _global = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_class_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    x = self._member + 1;
    }

    return _iterations * 10;
    }

    function test_write_class_explicit(x) {
    for (var i = 0; i < _iterations; ++i) {
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    self._member = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_class(x) {
    for (var i = 0; i < _iterations; ++i) {
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    x = _member + 1;
    }

    return _iterations * 10;
    }

    function test_write_class(x) {
    for (var i = 0; i < _iterations; ++i) {
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    _member = x + 1;
    }

    return _iterations * 10;
    }

    function test_read_local(x) {
    var _local = 0;
    for (var i = 0; i < _iterations; ++i) {
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    x = _local + 1;
    }

    return _iterations * 10;
    }

    function test_write_local(x) {
    var _local = 0;
    for (var i = 0; i < _iterations; ++i) {
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    _local = x + 1;
    }

    return _iterations * 10;
    }

    function test_nil(x)
    {
    for (var i = 0; i < _iterations; ++i) { }
    return _iterations * 10;
    }

    hidden var _timer;

    function onShow() {
    _timer = new Timer.Timer();
    _timer.start(self.method(:onTimer), 1000, true);
    }

    function onHide() {
    _timer.stop();
    _timer = null;
    }

    function onTimer() {
    var mode = _mode % _tests.size();
    _mode += 1;

    var operations = 0;
    var milliseconds = 0;

    var a = Sys.getTimer();

    do {
    operations += method(_tests[mode]).invoke(1);
    milliseconds = (Sys.getTimer() - a);
    } while (milliseconds < _min_milliseconds);

    _operations[mode] += operations;
    _milliseconds[mode] += milliseconds;

    Ui.requestUpdate();
    }

    hidden function value_for_cell(row, col) {

    if (row == 0) {
    return _cols[col];
    }
    else if (col == 0) {
    return _rows[row];
    }

    row -= 1;
    col -= 1;

    var cell = row * (_cols.size() - 1) + col;

    if (cell < _milliseconds.size() && _milliseconds[cell] != 0)
    { var time=_milliseconds[cell]*10000/_operations[cell];
    if (mode)
    { cell=cell&1;
    time-=_milliseconds[cell]*10000/_operations[cell];
    }
    return (time/10)+"."+(time%10)+"ms";
    }

    return "-";
    }


    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.clear();

    var rows = _rows.size();
    var cols = _cols.size();

    var font = Gfx.FONT_XTINY;

    var d = dc.getTextDimensions("0123456789", font);

    var dx = d[0] * 7 / 10.0; // average width of 5 digit
    var dy = d[1] * 1.0; // height of 1 digit

    var x = dc.getWidth() / 2;
    var y = dc.getHeight() / 2;

    x -= (dx * (cols - 1)) / 2;
    y -= (dy * (rows - 0)) / 2;

    for (var row = 0; row < rows; ++row) {
    var yj = y + (row * dy);

    for (var col = 0; col < cols; ++col) {
    var xj = x + (col * dx);

    var value = value_for_cell(row, col);
    if (value != null) {
    dc.drawText(xj, yj, font, value, Gfx.TEXT_JUSTIFY_RIGHT);
    }
    }
    }
    }
    }

    class MyBehaviour extends Ui.BehaviorDelegate
    {
    function initialize() { Ui.BehaviorDelegate.initialize(); }
    function onMenu() { return true; }
    function onSelect() { mode^=1; Ui.requestUpdate(); return true; }
    }

    class Test extends App.AppBase
    {
    function initialize() { AppBase.initialize(); }
    function getInitialView() { return [new MyView(), new MyBehaviour()]; }
    }[/CODE]
  • I wrote a small tool (windows only) which does a simple variable check of a source code file - it will show local/class/global access by color codes. It's just an idea to help tuning a program a little bit...

    Sreenshot and Program