How to reset a ChaiScript object to its initial state


#1

Jason,

First of all, thanks for the contributions you make to the C++ community. I discovered chaiscript by watching some of the Cppcon videos. I watched your presentation at this year’s cppcon which led me to the Chaiscript site. I have also watched a number of your weekly presentations on youtube, and read most of your best practices website at least a couple of times. All very helpful to me. Thanks for that.

I lead programming efforts on a commercial product that uses a built in scripting language. We built the ‘interpreter’ ourselves, based upon an old Dr. Dobbs Journal article. The code is working very well, but the language features on the script side are not conducive to maintainable scripts. It is more ‘basic’ like than C like. Since all of our source code is in C++, I have always wanted for a scripting engine that would allow for more C++ like code. Enter Chaiscript.

I am tinkering with Chaiscript as a migration path to deprecate our existing “Basic Script” functionality. I have been very pleased so far, but now I’m coming up with questions I can’t find answers to in the existing discussions.

I’ll start by how we use our existing scripting engine. Our end product does quality testing. We test a number of different types of components, and each type requires a different sequence of events. We orchestrate the sequence of events required to run a test with a specific script that we ‘execute’ with our scripting engine. Each time we execute a test, we tell our scripting engine to execute the appropriate script for the part being tested. Each time we start a script, the engine starts in a known initial state, and is though no script has been run. Beside there being different part types, there are many different ‘recipes’ of each part type. The contents of the recipe are loaded from a database into an object in our code. When a script runs, it has access to data held in that object which describes a specific part of the type that the script is designed to test.

I have gotten Chaiscript to a point where I can execute simple scripts, and exchange basic information. The specific issue I am running in to at this point is getting a chaiscript::ChaiScript object back to the initial state, so I can execute the same script again. This is somewhat simply overcome by re-creating the ChaiScript object. The problem is the creation process seems to be very expensive. It takes about 3 seconds to instantiate the Chaiscript object in my case. I’m not adding many of my functions yet, either. I don’t know if adding several hundred more things will make the creation process more expensive.

The path I chose was to create an object when my software starts up, and just keep it around for the life of the program. Then tell that object to execute a file whenever I start a test. That solved the problem of losing a few seconds while the Chaiscript object instantiated, but it cause the problem of not being able to run the same script over and over. Our existing scripting system goes back to carte-blanche state after completing an execution.

I have read what I can find on this issue, and I think I basically understand that this is not the way Chaiscript is designed to function. What I’m trying to do is figure out how if there is a different way to attack our problem, using Chaiscript the way it was designed.

( is it feasible to simply create a copy of the chai object after I do all of my adding, then copy that object before or after my execution? - or will the copy process take as long as the instantiation? )

For example, perhaps we should be executing a script that creates a state in the Chai object whereby we could trigger the execution of a test, instead of executing the script again. Maybe that would allow us to load all of our scripts into a chai object, then trigger which specific sequence we want to execute based upon part type. (We only have about 6 ‘types’ of parts, but potentially thousands of specific instance of the different part types). So if we could prepare the chai object by executing scripts that would create some operational state where we could prepare the specific data, then trigger a function or sequence within the chai object.

Perhaps our existing scripts are just organized wrong for how chai works. Our current scripts define variables and establish ‘state’, then execute a sequence of events based upon that state. Perhaps with chai we should execute a script that defines the state variables, then have script(s) that assign to the state variables (but don’t re-define them), and execute based upon the state variables.

I’m rambling at this point, but I don’t know how else to convey my struggle. I don’t think changes are needed to chaiscript as much as we need to figure out how to change our paradigm to fit how chaiscript operates.

If any of this makes sense, please attempt to give your thoughts on how you would apply chai to what we are trying to do.

And, keep up the good work.

Tom


#2

First: taking 3 seconds to instantiate ChaiScript is CRAZY CRAZY long. It should take a few milliseconds AT MOST. So I’m quite confused as to what is going on in your case.

Second: it seems you want the get_state and set_state methods. So something like:

chaiscript::ChaiScript chai;

// set up your base chai state who you want it

const auto base_state = chai.get_state();

// do some other stuff

chai.set_state(base_state);

See also: https://codedocs.xyz/ChaiScript/ChaiScript/structchaiscript_1_1ChaiScript__Basic_1_1State.html

If you can give me a self contained example of your code that takes several seconds to instantiate ChaiScript, I would very much like to see that.

Also, if you want any specific help integrating ChaiScript into your commercial code base, I am available for contracting.

Finally, I’ve started doing C++ training if you have an interest in bringing me in to your company for some on-site training for your team.

-Jason


#3

I have messed with get_state and set_state to no avail. If I re-instantiate the chaiscript::ChaiScript object each time I run my script, everything works OK. If the instantiation is milliseconds it will not be a problem, but that is not what I am experiencing at the moment. I am using code that looks much like the hello world example.

The time consumption is during the construction of chai in the code below. Once execution starts the speed seems reasonable. This is mess-around code where I was following your video with the ‘do_something’ example. In all cases I’m seeing very long times on the construction.

std::vector<std::string> module_paths; std::vector<std::string> use_paths; use_paths.push_back("e:\\Docs\\Visual Studio 2015\\Projects\\chai_test\\chai_test"); chaiscript::ChaiScript chai(chaiscript::Std_Lib::library(), module_paths, use_paths); chai.add(chaiscript::fun(&hello_world), "hello_world"); chai.add(chaiscript::fun(&do_something), "do_something"); try { // chai.eval_file("E:\\Docs\\Visual Studio 2015\\Projects\\chai_test\\chai_test\\test.c"); chai.eval_file("test.c"); }

In my production code, I’m trying to wrap the scripting in a class, and construct like this:

cscript::cscript(std::string path) : m_cs(chaiscript::Std_Lib::library(), std::vector<std::string>({}), std::vector<std::string>({ path })) { }

I tried get_state before the eval_file call, and set_state after completion, but that didn’t help with the redefinition errors when I re-run the script.

I have also not gotten the use paths to work for me. I am attempting to construct with my normal execution path and do the execute with filename only, and that doesn’t seem to be working for me.

I have run on visual studio on two different machines in the debug environment, and on a target machine as a debug build. The target machine is a much slower PC, and it is a debug build. It takes that machine about 3 seconds to start executing the script. On the development computer I am working on right now it appears to be a little under 2 seconds, and the memory consumption climbs rapidly during the instantiation process.


#4

I found that saving and restoring state and locals is what I needed to ‘clear’ the state.

`bool cscript::eval_file(std::string path)
{
const auto base_state = m_cs.get_state();
const auto base_locals = m_cs.get_locals();
bool success = false;
try
{
m_cs.eval_file(path);
success = true;
}
catch (const chaiscript::exception::eval_error &ee)
{
}

m_cs.set_state(base_state);
m_cs.set_locals(base_locals);

return success;

}`


#5

A debug build from inside of Visual Studio is the worst case scenario by far on any platform or compiler. I measure about 2 seconds from a cold start. This is because of how VS instruments everything that is happening during program execution.

Do you need this level of performance from a debug build inside of Visual Studio or do you run your tests from outside visual studio, or as release builds?

-Jason


#6

It looks like the debug build is indeed the culprit. On one of my development systems the construction drops from 1.2 seconds for a debug build to 0.007 seconds by going to a release build.

That effectively solves that problem.

We do a lot of remote debugging during development, so we spend a lot of time in debug builds. The slow construction is not an issue while we are debugging.

It appears that the overall execution speed of the script is severely affected by debug build as well. An empty for loop is about 100 times slower in a debug build. Not a show stopper for us, but we will have to keep it in mind.

for(var i = 0; i < 1000000; ++i){}

0.39 seconds release, 39 seconds debug


#7

If you were to use the currently unreleased “develop” branch you would see significant performance improvements in debug builds also. It is tested, but not quite as well proven as the last release.

It also requires VS2015 if that’s a problem or help to you.

-Jason


#8

I’m a long way from deploying anything in production code, so waiting on a formal release is not a problem. I can deal with the debug delays as long as I know what to expect in the end.
I’m also still thinking through whether we should make some changes to how we are doing things based upon the differences in how chai works compared to our existing scripting. The ability to easily configure a runtime environment in the scripting engine, instead of initializing everything each time we execute our scripts may have benefits. It opens up interesting possibilities.