Current potential performance issues in Nu, in no particular order -
Potential Issue - Event handlers in a dictionary are slower than handlers on the subscribed object a la C#. This means a look up for every publish. However, this is an artifact of a publisher-neutral event system rather than anything related to FP.
Possible Solution - A lot of optimization is already done to avoid publish calls that won't have a useful effect. Beyond these, I have yet to think of further solutions.
Potential Issue - Farseer Physics Engine doesn't scale to 1000s of interacting bodies - Genbox/VelcroPhysics#29
Possible Solution - Presumably Farseer could be replaced with a much faster 2D physics lib, perhaps one written in C or C++. Of course, the question then becomes about the overhead of the required marshalling.
Potential Issue - The string hashing required for each Xtension property look-up is suboptimal.
Possible Solution - Not many practical ones. This issue wouldn't exist if .NET lazy-cached hashes in strings, but there's no reason to believe it ever will. At one point I used an alternative type to string called 'Lun' (later called 'Name') which contained a string and its eagerly-computed hash, but it wasn't very friendly to use. I decided to get rid of it in favor of .NET strings to simplify Nu's API. I'm pretty sure this was the right decision, but I can't prove it one way or another without making large speculative changes to the engine.
Update - Now that F# finally has implicit ctors, reintroducing the Name type shouldn't cause as many changes as it previously would have. This might now be a practical experiment to run.
Potential Issue - LOH threshold is perhaps too small.
Possible Solution - Upgrading to >= .NET 4.8 will allow us to configure it via GCLOHThreshold
- https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gclohthreshold-element
Update - I've tried increasing LOH, but I cannot observe it having any effect. It's like my attempt is being ignored by the runtime.
Potential Issue - Potentially a lot of events when a subscribed entity transforms -
|
let world = World.publishEntityChange (nameof Transform) () () publishChangeEvents entity world // OPTIMIZATION: eliding data for computed change events for speed. |
|
let world = |
|
if is2d then |
|
let perimeterCenteredChanged = transformNew.PerimeterCentered <> transformOld.PerimeterCentered |
|
let perimeterChanged = positionChanged || scaleChanged || offsetChanged || sizeChanged || perimeterCenteredChanged |
|
let boundsChanged = perimeterChanged || rotationChanged |
|
if boundsChanged then |
|
let world = World.publishEntityChange Constants.Engine.BoundsPropertyName () () publishChangeEvents entity world |
|
let world = |
|
if perimeterChanged then |
|
let world = World.publishEntityChange (nameof transformNew.Perimeter) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.PerimeterUnscaled) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.PerimeterCenter) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.PerimeterBottom) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.PerimeterBottomLeft) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.PerimeterMin) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.PerimeterMax) () () publishChangeEvents entity world |
|
world |
|
else world |
|
let world = if positionChanged then World.publishEntityChange (nameof transformNew.Position) transformOld.Position transformNew.Position publishChangeEvents entity world else world |
|
let world = if scaleChanged then World.publishEntityChange (nameof transformNew.Scale) transformOld.Scale transformNew.Scale publishChangeEvents entity world else world |
|
let world = if offsetChanged then World.publishEntityChange (nameof transformNew.Offset) transformOld.Offset transformNew.Offset publishChangeEvents entity world else world |
|
let world = if sizeChanged then World.publishEntityChange (nameof transformNew.Size) transformOld.Size transformNew.Size publishChangeEvents entity world else world |
|
let world = if perimeterCenteredChanged then World.publishEntityChange (nameof transformNew.PerimeterCentered) transformOld.PerimeterCentered transformNew.PerimeterCentered publishChangeEvents entity world else world |
|
world |
|
else world |
|
else |
|
let boundsChanged = positionChanged || rotationChanged || scaleChanged || offsetChanged || sizeChanged |
|
if boundsChanged then |
|
let world = World.publishEntityChange Constants.Engine.BoundsPropertyName () () publishChangeEvents entity world |
|
let world = if positionChanged then World.publishEntityChange (nameof transformNew.Position) transformOld.Position transformNew.Position publishChangeEvents entity world else world |
|
let world = if scaleChanged then World.publishEntityChange (nameof transformNew.Scale) transformOld.Scale transformNew.Scale publishChangeEvents entity world else world |
|
let world = if offsetChanged then World.publishEntityChange (nameof transformNew.Offset) transformOld.Offset transformNew.Offset publishChangeEvents entity world else world |
|
let world = if sizeChanged then World.publishEntityChange (nameof transformNew.Size) transformOld.Size transformNew.Size publishChangeEvents entity world else world |
|
world |
|
else world |
|
let world = |
|
if rotationChanged then |
|
let world = World.publishEntityChange (nameof transformNew.Rotation) transformOld.Rotation transformNew.Rotation publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.Angles) () () publishChangeEvents entity world |
|
let world = World.publishEntityChange (nameof transformNew.Degrees) () () publishChangeEvents entity world |
|
world |
|
else world |
|
let world = |
|
if elevationChanged |
|
then World.publishEntityChange (nameof transformNew.Elevation) transformOld.Elevation transformNew.Elevation publishChangeEvents entity world |
|
else world |
|
let world = |
|
if overflowChanged |
|
then World.publishEntityChange (nameof transformNew.Overflow) transformOld.Overflow transformNew.Overflow publishChangeEvents entity world |
|
else world |
Possible Solution - Probably nothing great. Could selectively disable a chunk of transform events depending on the application. Not real sure what to do here other than assess that this is part of the cost of doing business declaratively.
Potential Issue - Synchronizing entity properties via World.setEntityPropertyFast
requires a small and likely cache-local dictionary look-up via WorldModuleEntity.EntitySetters
, which is surprisingly fast.
Possible Solution - A faster alternative might be hard-coding a duplicate of the EntitySetters table in a match expression or using a loftier technique such as code generation in the MVU implementation.
Potential Issue - Nu Text rendering might be quite inefficient due to not caching target render buffers. IIRC, render buffers use for text are allocated and deallocated on a one-off basis. I do not see how that could possibly scale well.
Possible Solution - Code it properly. :)
Potential Issue - Only seems to cause a couple small hiccups at the start of programs, but currently .NET GC compaction is not yet parallelized and therefore can cause stalls while it does its thing. This doesn't seem to happen once Nu programs hit their steady state after a couple seconds. Fortunately, according to the .NET team, it appears that parallel compacting is being implemented.
Possible Solution - Wait for parallel compacting GC to ship (.NET 9?). Otherwise, issue GC.Collect between scenes if needed to.
Potential Issue - Bullet Physics out-of-box kinematic character controller does not scale beyond a dozen instances or so.
Possible Solution - Maybe we can roll our own faster kcc somehow?
Potential Issue - Setting physics properties after creating an entity, such as is done by the MMCC initializers, can cause a lot of body recreation inside the physics engines due to the way that RigidBodyFacet's property change handlers work.
Possible Solution - Instead of recreating the physics bodies, create addition body property synchronization messages to make body recreation less often necessary.