Perhaps by splitting the tasks to the other cores of the CPU (multithreading, parallel programming) and reducing the complexity of nasal loops you could increase the FPS, I don't know if multithreading is allowed in nasal or if all the tasks can be run only in sequential mode on a single CPU core (limitations of flightgear which prevent to optimize further more the nasal code).
Multi-threading in Nasal is supported and possible - however, you could basically say that can only use it properly if you have a strong background in programming and/or in FlightGear internals - otherwise, it's unlikely that people can use it successfully - because the rest of FlightGear is basically single-threaded, and that also applies to all extension functions and FlightGear specific Nasal APIs.
Basically, it's not feasible to introduce multi-threading at a later time, unless you have a strong background in software engineering. However, designing your systems with threading in mind upfront can work out reasonably well - but obviously you need to work out the data flow and dependencies between different code routines.
The only person to really optimize all of the aircraft, including Nasal code, is Richard and he happens to be a FlightGear core developer, and also has a background in computer science - and certainly must have been using FlightGear for 10+ year
However, Richard has written a number of helpers which he is in the process of sharing, i.e. adding to fgdata - so that others, less familiar with fg internals, can also use these helpers.
Speaking in general, you almost certainly don't want to use multi-threaded Nasal code - unless you have a corresponding background, or at least know Nasal/the property tree and the SGSubsystem architecture inside out.
Sooner or later, it seems rather likely that Canvas based avionics may optionally get their own/private Nasal instance (and property tree) per Canvas - so that the corresponding scripts can run outside the FlightGear main loop. Under the hood, a Canvas is already primarily a property tree - one that watches certain properties/locations, and that maps reads/writes to the corresponding OSG APIs.
Proceeding "a is" simply isn't viable for FlightGear as a whole, because the way Nasal and the Canvas system work, there is more and more rendering code tied to non-deterministic code (among others due to Nasal's garbage collector). However, Canvas based avionics have a well-defined set of inputs (think properties, and calling certain FG extension functions, e.g. to query the navdb) and well defined outputs (usually just a single FBO/RTT texture).
Thus, conceptually the code used to update such a texture does not need to run inside the FlightGear main loop. Especially when keeping in mind that a typical cockpit may have 6+ of these FBO textures, all of which are currently updated by Nasal script running at frame rate inside the main loop.
The shared requirement these update routines is that they require a state vector of properties and API calls - some are fixed (i.e. always the same properties), whereas others are dynamic and may change depending on the mode/context in question (imagine showing different modes/elements of a PFD/ND).
However, our experience when designing the MapStructure/ND frameworks has been that regardless of the number of instruments, it isn't feasible to always poll/getprop properties during each update cycle - instead, it makes sense to use memoization (caching) - Richard came up with a dedicated framework for that, and also for splitting work across multiple frames.
This is an approach that the MapStructure framework also used (instead of threading).
Thus, if you were to render n 10+ instances of a ND, it would be kinda pointless to do so at frame rate while always polling /position/*, /orientation, and /fdm/* - likewise, using listeners would not be a good idea for state that changes per frame.
In other words, what's needed is a partioning mechanism to subscribe to relevant state, and then only do the fetching once (per update cycle), where all subscribing avionics would merely get a copy of the state, rather than each doing their Nasal/C++/Nasal context switches for each extension function call.
Richard has worked out a generic scheme to accomplish exactly that (see his fgdata commits to /Nasal).
In the mid-term, it will make sense to have the discussion if, and how, to specify lists of relevant properties for each canvas - which would include output and input properties. At that point, the Canvas system itself could traverse that list at the CanvasMgr level, and provide each canvas texture with a copy of fresh state, without unnecessarily causing property tree "traffic".
At that point, the setup would resemble the "instant replay/flight recorder" subsystem - because that, too, is using a configuration scheme to encode relevant I/O properties (per aircraft).
The thing is, once you have this sort of info PER INSTRUMENT (per canvas), you can trivially use a dedicated SGPropertyNode per Canvas texture, and then also hook up a dedicated FGNasalSys instance to the Canvas texture in question.
With this sort of setup, you'll then end up with an off-screen RTT/FBO context that can be asynchronously updated in the background, without having to run inside the main loop - you would even have to tell OSG to only run it at ~30 hz, because it could easily run at 100+ hz otherwise, i.e. updating a texture unnecessarily.
The kind of coding to populate/update and render such a canvas texture would be a little different compared to what people are currently doing, but it would be well worth it - because you could literally have 10+ canvas textures computed/updated and generated outside the main loop, with the only required synchronization point being the list of subscriptions (properties, and FlightGear APIs) - and obviously the final stage where some locking will be required to fetch the generatede Canvas FBO from the OSG worker thread.
And this, too, could be facilitated by some work that Richard has shared with the community, namely "Emesary" - which he is already using to hook up a threaded garbage collector to Nasal/FlightGear.
Admittedly, all of this may seem a little roundabout and maybe even complex - but short of using a simple "worker thread" setup, threading Nasal code is unlikely to work due to the sheer number of architectural restrictions in FlightGear itself.
Fixing up the Canvas system to support an optional mode where the property tree is a private one, not shared with the main fgfs tree, is comparatively straightforward however - right now, Nasal itself is integrated in the form of a singleton, so that would need to change - but that's under way already, due to unit testing work that bugman has been working on recently.
More recently, Jules has been working on CompositeViewer support, which is another promising option - because compositeviewer means that a completely independent scene graph can be easily processed and shown, without cluttering up the main loop.