Poor performance with Android NDK


#1

Hi everyone.

I want to use ChaiScript as primary in my project. Everything works amazing on iOS and Emscripten. But on Android I have issues.
I have critical lags(Android NDK 20C or any other).
Any call of chaiscript from c++ is very expensive(or break/continue during loops). Even empty function call takes about 25ms on Samsung S7.

I settled up profiler and found that NDK goes crazy when ChaiScript want to execute code like this:

throw detail::Return_Value{void_var()};

Throwing exception kills all performance.
Stack is like this:

soinfo::gnu_addr_lookup(void const*)
....
dlladdr
helper(_Unwind_Context*, void*)
__wrap___cxa_throw

As I understand this part responsible for unwinding stack during throw execution.
If I replace “throw” to “return” and add std::stack to hold return values execution takes < 1ms.

it’s not a trivial case to rewrite code without using “throw”.
Does anybody have any suggestions?

Thanks in advance,
Dmitry


#2

That is unfortunate that they are crippling the performance of exceptions on the latest NDK.

It’s also unfortunate that a very early design decision on how to handle control flow in ChaiScript means that a “return” is handled by exceptions. (this is to ensure proper call stack unwinding).

The built in optimizer removes return statements where possible to try and reduce this problem, but that mechanism could likely be enhanced.

Can you give an example of the script you are executing so I can see if it might be reworked to reduce the issues?


#3

Thank yo for your reply.

You can reproduce using following code:

  chaiscript::ChaiScript s;
    const char* const cstr =
    R"(
    
    def k()
    {
        return 10;
    }
    
    def f()
    {
       k();
       return true;
    }
    
    )" ;
    
    s.eval(cstr);
    
    auto start = std::chrono::high_resolution_clock::now();
    s.eval("f()");
    auto stop = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
    LOG("Duration %d", duration.count());

I use such Application.mk script settings:

APP_OPTIM := release

NDK_TOOLCHAIN_VERSION := clang
APP_STL := c++_static
APP_CPPFLAGS += -fexceptions 
APP_CPPFLAGS += -frtti 
APP_CPPFLAGS += --std=c++14
APP_CFLAGS += -Wno-error=format-security
ifeq ($(BUILD_TYPE),x86)
	APP_PLATFORM := android-15
	APP_ABI := x86
	TARGET_CPU_ABI  := x86
else ifeq ($(BUILD_TYPE),arm64)
	APP_PLATFORM := android-15
	APP_ABI := arm64-v8a
	TARGET_CPU_ABI  := arm64-v8a
else 
	APP_PLATFORM := android-15
	APP_ABI := armeabi-v7a
	TARGET_CPU_ABI  := armeabi-v7a
endif

For me I’m getting 52ms for this test for “arm64-v8a” and “armeabi-v7a”.

Thanks!


#4

Both of those examples should be automatically optimized in the last releases of ChaiScript. Can you verify which version you’re using?


#5

I use last available release:
static const int version_major = 6;
static const int version_minor = 1;
static const int version_patch = 0;

They should be optimized after the first call? or at evaluation ?


#6

At script parse time (as far as I know ChaiScript is the only language that does this).

The code for it is here: https://github.com/ChaiScript/ChaiScript/blob/release-6.x/include/chaiscript/language/chaiscript_optimizer.hpp#L75-L95


#7

Thanks for info!
But in other cases it doesn’t save us from lags.
Thow is used in 3 places - “return”, “break”, “continue”.
Can we avoid use throw and replace it with something else?


#8

I would love to remove it and replace it. I’ve spent the last 8 years or so regretting that design decision and trying to minimize the impact. If you can come up with some other way, please let me know. It’s possible I lack the imagination to see a better option that works within the current framework.


#9

Thank you a lot for you help. I’ll try to figure out how to avoid this!
If I’ll find solution I let you know!

Cheers,
Dmitry