2

DESCRIPTION

I am currently designing an architecture for a C# multiagent simulation, where agent actions are driven by many modules in their "brain", which may read sensors, vote for an action or send messages/queries to other modules (all of this is implemented through the exchange of messages). Of course, modules can have a state.

Modules run in parallel: they have an update method which consumes messages and queries, and perform some sort of computation. The update methods return iterators, and have multiple yields in their bodies, so that I can schedule modules cooperatively. I do not use a single thread for each module because I expect to have hundreds to thousands of modules for every agent, which would lead to a huge amount of RAM occupied by thread overhead.

I would like these modules to behave like runtime plugins, so that while the simulation is running I can add new module classes and rewrite/debug existing ones, without ever stopping the simulation process, and then use those classes to add and remove modules from the agents' brains, or just let existing modules change their behaviours due to new implementations of their methods.

POSSIBLE SOLUTIONS

I have come up with a number of possible solutions in the last few days, which all have something disappointing:

  1. Compile my modules into DLLs, load each in a different AppDomain and then use AppDomain.CreateInstanceFromAndUnwrap() to instantiate the module, which I would then cast to some IModule interface, shared between my simulation and the modules (and implemented by each module class). The interface would expose just the SendMessage, the Update and a few other members, common to all modules.

    • The problem with this solution is that calls between AppDomains are much slower than direct calls (within the same AppDomain).
    • Also, I don't know the overhead of AppDomains, but I suppose that they are not free, so having thousands could become a problem.
  2. Use some scripting language for the modules, while keeping C# for the underlying engine, so that there is no assembly loading/unloading. Instead, I would host an execution context for the scripting language for each module.

    • My main concern is that I do not know a scripting language which is big (as in 'python, lua, ruby, js are big, Autoit and Euphoria are not') fast, embeddable into .NET and allows step by step execution (which I need in order to perform cooperative scheduling of module execution).
    • Another concern about this is that I suppose I'd have to use a runtime context for each module, which in turn would have massive overhead.
    • Lastly, I suppose a scripting language would be probably slower than C#, which would reduce performance.
  3. Avoid unloading of assemblies, instead renaming/versioning them somehow, so that I can have a ton of different versions, then just use the latest one for each type.

    • I'm not even sure this is possible (due to omonimous types and namespaces)
    • Even if possible, it would be very memory-inefficient.
  4. Do a transparent restart of the simulation, which means pausing the simulation (and execution of the scheduler of brains/modules), serializing everything (including every module), exiting the simulation, recompiling the code, starting the simulation again, deserializing everything, catching any exception raised due to the changes I made to the class and resuming execution.

    • This is a lot of work, so I consider it my last resort.
    • Also, this whole process would be very slow at some point, depending on number of modules and their sizes, making it impractical

I could overcome this last problem (the whole process in solution 4 becoming slow), by mixing solutions 3 and 4, loading many many assemblies with some form of versioning and performing a restart to clean up the mess every now and then. Yet, I would prefer something that doesn't interrupt the whole simulation just because I made a small change in a module class.

ACTUAL QUESTION

So here is my question(s): is there any other solution? Did I miss any workaround to the problems of those I found? For example, is there some scripting language for .NET which satisfies my needs (solution #2)? Is versioning possible, in the way I vaguely described it(Solution #3)?

Or even, more simply: is .NET the wrong platform for this project? (I'd like to stick with it because C# is my main language, but I could see myself doing this in Python or something alike if necessary)

Daniele
  • 21
  • 1

2 Answers2

1

Did you consider Managed Extensibility Framework?

spender
  • 117,338
  • 33
  • 229
  • 351
  • I did now, but it appears MEF loads every assembly into the current AppDomain, which makes it impossible to unload them without stopping the application. Did I miss something? – Daniele Jan 22 '14 at 02:30
  • @Daniele : perhaps MAF is a better way forward: http://stackoverflow.com/questions/835182/choosing-between-mef-and-maf-system-addin – spender Jan 22 '14 at 07:28
  • yes, it is, but it uses multiple AppDomains, which make the calls three orders of magnitude slower.I'm starting to think that I either abandon C# or accept that I have to restart my application every now and then. – Daniele Jan 22 '14 at 12:30
0

I'm working in a simulation system that works in a very similar way, treating agent modules as plugins.

I created a Plugin Manager that handles every Domain loading related things, checking plugin validity in a dummy domain and then hotloading it in the engine domain.

Using AppDomain is where you can get the full control, and you can reduce process time by running your Plugin Manager's tasks in parallel.

AppDomains aren't cost free, but you can handle it using only two (or three if you need more isolation between validation and execution domains).

Once a plugin file is validated you can load it in the very main process at any time, creating a shadow copy in any domain's probing path (or in dynamic path if set) and targeting it instead of original file is useful to check versioning and updates.

Using a domain for validation and another to execution may require a swap context, who takes care of previous version instances while updating.

Keeping a time scheduled task to check new plugins and new versions, and then block plugin module usage, swap files, reload, and unblock, reinstancing new versions from previous if necessary.