Wednesday, August 31, 2016

Ponder Design Review


Review of  function features

The Ponder library is a fork of CAMP and takes its design decisions from there. The biggest change was the removal of Boost, which should leave the functionality of the API unchanged.

CAMP has some interesting features, e.g. in Function we can assign a function callback to test whether a function is currently callable.

  /**
   * \brief Set the callable state of the current function with a dynamic value
   *
   * function can be any C++ callable type, and will be called to return the
   * callable state of the function each time it is requested. This way, the callable
   * state of a function can depend on metaclass instances.
   *
   * \param function Function to call to get the callable state of the function
   *
   * \return Reference to this, in order to chain other calls
   */
  template <typename F>
  ClassBuilder<T>& callable(F function);


I'm not sure of the rationale of some of the features. This is a feature I have not used. There are other features, like parent-child user objects, again unused. These features may have a use in a particular application, but they might not be viewed as widely used. So, perhaps they should not be so tightly coupled with the function data.

Design is choice

As Andrei Alexandrescu says, "Design is choice". There may be many solutions to problem, but the design is the one you chose.

In CAMP the data of an object is mixed with its use, e.g. function data also contains methods to call the function. There may be different ways in which we want to call the function. The current call method takes a dynamic array of arguments which are value types. This is quite inefficient, along with the value mapping that occurs, where many of the objects may be copied.

CAMP call behaviour has several particular traits:
  • Coercion of values through ValueMapper.
  • Calling with dynamic array of values.
  • Ability to block calls (callable).
It might be best to separate the call behaviour from the type information. This way calling, and any other uses of the type, can be customised for its use. This is a significant change away from CAMP.

I am currently extending Ponder with Lua scripting ability. This has been complicated by the Ponder value mapping and its difficulty in dealing with the ambiguity of references.

Type data is immutable

Type information is static. It is baked into the program at compile-time. The Ponder types should reflect this. Any uses of the data should refer to the data, but not modify it.


Ponder: what is reflection?

This is discussion on the current state of Ponder and thoughts on future changes.

What is reflection?

Wikipedia states:
In computer science, reflection is the ability of a computer program to examine, introspect, and modify its own structure and behavior at runtime
and uses are:
... observing and modifying program execution at runtime. A reflection-oriented program component can monitor the execution of an enclosure of code and can modify itself according to a desired goal related to that enclosure. This is typically accomplished by dynamically assigning program code at runtime. 
In object-oriented programming languages such as Java, reflection allows inspection of classes, interfaces, fields and methods at runtime without knowing the names of the interfaces, fields, methods at compile time. It also allows instantiation of new objects and invocation of methods. 
Reflection can be used to adapt a given program to different situations dynamically. Reflection-oriented programming almost always requires additional knowledge, framework, relational mapping, and object relevance in order to take advantage of more generic code execution.
Features we might expect are:

Type Introspection

The ability to introspect a program type. E.g. see what type it is and which members it contains. This might useful for runtime data binding, e.g. loading an XML file and assigning the values to class members based on element name matches.

Some C++ reflection systems offer this data automatically by parsing the symbols in a compiled C++ file. Ponder does not offer this, and there is some discussion of this in a previous post. It is generally thought that you do not want to export all data, and that sometimes the data needs annotating in order to remove ambiguity. For example, function returning references: should the values be copied or kept as references?

Self Modification

Since C++ is statically compiled, self modified code might be limited to setting pointers and callbacks to chosen type. It might be possible by implementing a runtime dynamic C++ compiler is complicated, and also likely something you would't want to distribute with your program. A more popular way would be to customise behaviour with data, or use an embeddable scripting language, perhaps with dynamic features, e.g. Lua.


Tuesday, August 16, 2016

Gwork Continuous Integration

I added a Null renderer to Gwork, i.e. one that doesn't draw anything. This makes it easier to do things like cross-platform build testing. We might check several different configs of the build without having to link to a graphical API.

This is useful because if anyone submits any patches to Gwork they will be tested in the pull-request queue. Users can also add tests builds their own Travis accounts so they can see if their fork is building.

Travis

For Linux and MacOS (OSX) builds I used Travis. It is a free service, so I can't complain too much, but it took a considerable amount of fiddling around to get Linux builds working. I won't bore you with the details.

Travis current live build status:




Monday, August 15, 2016

Gwork memory allocation stats

I added memory allocation tracking to Gwork as I'd like to keep track of the number and size of allocations. A CSV file is generated which parsed to produce a report. The following are tables from the report.

Current state

The current Gwork allocation stats from:

commit a95a0fde3afb68d2fd4d7af817159c4891db970d
AuthorDate: Mon Aug 15 20:26:21 2016 +0100


NameAlloc countAlloc size
API test90181566856 (1530.133KB)
Button5311024 (10.766KB)
Checkbox346088 (5.945KB)
CollapsibleList20839744 (38.812KB)
ColorPicker28132680 (31.914KB)
ComboBox4194755408 (737.703KB)
CrossSplitter5411128 (10.867KB)
GroupBox133928 (3.836KB)
ImagePanel61248 (1.219KB)
Label458168 (7.977KB)
LabelMultiline368736 (8.531KB)
ListBox25246040 (44.961KB)
MenuStrip817153304 (149.711KB)
Numeric395056 (4.938KB)
PageControl5811568 (11.297KB)
ProgressBar729728 (9.500KB)
Properties60883240 (81.289KB)
RadioButton6212072 (11.789KB)
ScrollControl602113640 (110.977KB)
Slider345040 (4.922KB)
StatusBar102000 (1.953KB)
TabControl18134016 (33.219KB)
TextBox23423080 (22.539KB)
TreeControl714117984 (115.219KB)
Window142712 (2.648KB)


GWEN stats

NameAlloc countAlloc size
API test100511721899 (1681.542KB)
Button6211736 (11.461KB)
Checkbox356152 (6.008KB)
CollapsibleList22940952 (39.992KB)
ColorPicker34136256 (35.406KB)
ComboBox4742792208 (773.641KB)
CrossSplitter6315352 (14.992KB)
GroupBox1510264 (10.023KB)
ImagePanel81408 (1.375KB)
Label539304 (9.086KB)
LabelMultiline4118920 (18.477KB)
ListBox28966771 (65.206KB)
MenuStrip983203992 (199.211KB)
Numeric445216 (5.094KB)
PageControl6412096 (11.812KB)
ProgressBar7310016 (9.781KB)
Properties66180348 (78.465KB)
RadioButton6812592 (12.297KB)
ScrollControl574105864 (103.383KB)
Slider344912 (4.797KB)
StatusBar112144 (2.094KB)
TabControl18241952 (40.969KB)
TextBox30637552 (36.672KB)
TreeControl727121572 (118.723KB)
Window162856 (2.789KB)

As you can see the "unit test" from GWEN use 1681.542KB, with Gwork using 1530.133KB. So that's roughly 160KB smaller. Note, this is from the gwen branch in the Gwork repo. 

Comparison

There is a more detailed comparison below.

NameCount deltaSize delta% size
API test-1063-163051 (-159.229KB)90.5%
Button-9-71293.9%
Checkbox-1-6499.0%
CollapsibleList-21-1208 (-1.180KB)97.1%
ColorPicker-64-3752 (-3.664KB)89.7%
ComboBox-548-36800 (-35.938KB)95.4%
CrossSplitter-9-4224 (-4.125KB)72.5%
GroupBox-2-6336 (-6.188KB)38.3%
ImagePanel-2-16088.6%
Label-8-1136 (-1.109KB)87.8%
LabelMultiline-5-10184 (-9.945KB)46.2%
ListBox-37-20731 (-20.245KB)69.0%
MenuStrip-166-50688 (-49.500KB)75.2%
Numeric-5-16096.9%
PageControl-7-77693.6%
ProgressBar-1-28897.1%
Properties-532892 (2.824KB)103.6%
RadioButton-6-52095.9%
ScrollControl4200100.2%
Slider0128102.6%
StatusBar-1-14493.3%
TabControl-1-7936 (-7.750KB)81.1%
TextBox-72-14472 (-14.133KB)61.5%
TreeControl-13-3588 (-3.504KB)97.0%
Window-2-14495.0%

There isn't much detail about where the savings come from but the unicode string changes will have had an effect. GWEN stored every control string as wide unicode and as ASCII. LabelMultiline has a considerable saving, probably due to this. Interesting that a couple are larger than GWEN. Will have to investigate this.

Future Work

This gives a reference point on which to compare any future memory saving work. Work here might include:

  • Event system refactor. This is pretty inefficient as every control contains listeners and the associated containers whether used or not.
  • Type size and reordering. E.g. booleans might be better as chars, enums as chars etc. These might also be more efficient packed in the controls by reordering them.