JS Field optimization

Jan 22 at 9:40 AM
So, I've been making a game environment in C# and I am using Jurassic for the JS engine. A game draws textures to screen which are colored. Colors are stored as bytes. I have a JS color object with 4 fields, red, green, blue, and alpha. They are of type Object (but JS Number's go into them).

This is how I go (unsafely since I'm assuming here) from a Number object to a byte:
_color.R = (byte)(Convert.ToInt32(this["red"]) % 256);
//...
As you can see, that is fast, but not as fast as:
_color.R = (byte)(red % 256);
//...
But the above can only be done with JSField, which currently uses consts. So, if the top method is the only way (I need it for dynamic color changing), then how can I, on the C# side of things, make custom getters and setters for those 4 fields? This would really help a lot to speed this up. And yes, profiling my code reveals this as a hotspot, as because every image drawn to screen might convert it's color object each time (imagine objects fading out).

Now, if the coloring were in getters and setters that'd help a bit, since not always all channels are updated and not always all the time. It would help a lot more if I could just read in direct int values from the fields.

I think this is a worthy discussion since I don't see how else to do it.
Jan 22 at 9:56 AM
Edited Jan 22 at 10:05 AM
I solved it.
[JSProperty(Name="red")]
public int Red {
    get { return _color.R; }
    set { _color.R = (byte)value; }
}
This works quite well! I guess this is equivalent to setting up those getters/setters I talked about. I think there should be a tutorial on how to do this (at least this is here for the discussion readers). I had to peruse the Jurassic source code to see this, and make use of it. :P

Though I say I'm sure we can make it faster. I'm not just making a game engine I'm creating a new version of an existing one and it too uses JS. It was much faster at getting/setting object properties than Jurassic. In a loop with all 4 fields being modified I'm back at square one. But that's not always a common scenario. Even then it still bugs me other embedded JS environments incur zero performance loss setting values tied to host objects (like how this one is tied to a color).

I still think there is a way of getting values back straight from properties that are the types you need. I think [JSField] needs to be completed. ;)
Jan 22 at 11:18 AM
Uh-oh, now when I create these color objects it's literally 1000 times slower. :(

Why can't there be a good alternative? Other embedded JS environments handle fields wickedly fast. There has got to be a way in Jurassic.
Coordinator
Jan 26 at 9:18 PM
Edited Jan 26 at 9:18 PM
Have you tried this instead?
public int Red {
    get { return (byte)Convert.ToInt32(this["red"]); }
    set { this["red"] = (int)value; }
}
Jan 26 at 11:33 PM
Edited Jan 27 at 12:13 AM
No, sadly I need to expose an RGBA color object to the JS environment. It get's modified there and then used in the C# environment.
var color = new Color(255, 255, 255, 255);
color.red = 154; // manipulations etc.

CLR_object.setColor(color);
When the CLR object gets the color it doesn't just use the red channel or the blue channel, but all channels, so your above getter/setter doesn't help. My goal here was so that on the JS side, setting the red field modified the red channel only.

What I had been doing was this:
class Color : ObjectInstance
{
    private MyColor _color;
    //...

    public MyColor ToColor() { // I take a performance hit here:
        _color.R = Convert.ToByte(this["red"]);
        _color.G = Convert.ToByte(this["green"]);
        _color.B = Convert.ToByte(this["blue"]);
        _color.A = Convert.ToByte(this["alpha"]);
        return _color;
    }
}
I then moved to this, it was much faster:
    private MyColor _color;

    [JSProperty(Name = "red"]
    public int Red // I take a smaller performance hit here:
    {
        get { return _color.R; }
        set { _color.R = (byte)value; }
    }

    //...

    public MyColor ToColor() {
        return _color;
    }
The above makes colors quicker to use on the CLR side since I'm not converting everything all the time. It's only converting when 'red' property is modified on the JS side. This is a 100% awesome fix, especially since Convert is not being used. But, as my last post indicates, the Color Object becomes 1000 times slower to construct. Not only do I need to modify the rgba properties, but I also need to have a fast constructor as well. Now, the first method (without getters and setters) the object is fast to construct, but slower to use.

Now... you do have a JSField attribute, I wonder if that'll help.
    private MyColor _color;

    [JSField]
    public int red;

    //...

    public MyColor ToColor() {
        _color.R = (byte)red;
        return _color;
    }
But currently you only support readonly or const types for JSField. It'd be nice to see this for mutable fields too. That way I'm not 'guessing' the data type for the field, which considerably slows down object creation and manipulation. Calling Convert() many times is bad and reducing my use of that is good. MyColor uses bytes, but the JS wrapper only doubles and ints, so this is where I'd like to improve upon.

I think this project is awesome for this kind of stuff, and I'm so close to getting good performance out of it (over V8 environments that take a huge CLR to JS performance hit when calling methods, etc.)
Coordinator
Jan 27 at 9:17 PM
Edited Jan 27 at 9:18 PM
I'm guessing the PopulateFunctions call is to blame for the constructor performance hit. That method does some heavy reflection to find the attributes, which is likely why you are seeing the slowdown. I suggest you abandon PopulateFunctions() and [JsFunction]/[JsProperty] attributes and hook up any methods you need manually.

You can use DefineProperty to do this. Check the source code for PopulateFunctions here to see how it does it: https://jurassic.codeplex.com/SourceControl/latest#Jurassic/Library/Object/ObjectInstance.cs
Unfortunately, this code uses ClrFunction which is an internal class, so you can either make it public or you can derive your own class from FunctionInstance.

By doing this you can avoid using reflection entirely, which should result in a good speed-up (at the expense of significantly more complicated code). For further gains you could cache the PropertyDescriptor instances in static fields.
Jan 27 at 10:52 PM
Woah, that totally worked, thanks! PopulateFunctions was the thing making it slow. Yes, it might be more code but thankfully these color objects are the only thing required to be built and used fast, wrappers around images, etc. can be slow. I still have to use Convert, sadly. I decided to derive my own function instance for getters and setters and had to do this:
    public class ColorSetter : FunctionInstance
    {
        int _type;
        public ColorSetter(ScriptEngine engine, int type)
            : base(engine)
        {
            _type = type;
        }

        public override object CallLateBound(object thisObject, params object[] argumentValues)
        {
            var color = (ColorInstance)thisObject;
            int v = Convert.ToByte(argumentValues[0]); // hotspot
            switch (_type)
            {
                case 0: return color.R = v;
                case 1: return color.G = v;
                case 2: return color.B = v;
                case 3: return color.A = v;
            }
            return v;
        }
    }
But anyways it still only converts just a single value, which is not bad at all. The FPS of a color-heavy loop went from 2000 to 8600, so I'm happy. The slowdown returns if all 4 RGBA properties are being modified, but that's not so bad since that very rarely happens, and since colors are now once again fast to make, it'd be beneficial to remake the colors in those instances instead. :)

One more thing, how do you add a custom function? I can see using your method to add properties and getters/setters, but what about a method?
Coordinator
Jan 28 at 10:59 AM
I still have to use Convert, sadly.
Have you tried this?
int v = TypeConverter.ToInt32(argumentValues[0])
I don't know how Convert.ToByte is implemented, but I think TypeConverter.ToInt32 will be faster because it only has to deal with a limited number of types.
One more thing, how do you add a custom function? I can see using your method to add properties and getters/setters, but what about a method?
That's rather easy:
this["functionName"] = new FunctionInstanceDerivedClass();
Or equivalently:
DefineProperty("functionName", new PropertyDescriptor(new FunctionInstanceDerivedClass(), PropertyAttributes.FullAccess), false);
Internally, you can think of properties being stored in a Dictionary<string, PropertyDescriptor>, where a PropertyDescriptor has a value or a getter and setter (along with a flags value that indicates whether the property is writable and/or configurable). In theory you could call PopulateFunctions on the first MyColor instance, then for subsequent MyColor instances just copy the property values across from the first instance (using ObjectInstance.Properties and ObjectInstance.DefineProperty).

It's quite easy to see that if you derived a class from FunctionInstance that took a delegate, and in CallLateBound() you converted all the parameters to the right types and then called the delegate with those converted parameters, you could then wrap any method so that it was callable from JavaScript. That's exactly what ClrFunction does; the only really tricky part is doing the parameter conversion in an efficient way.
Jan 29 at 12:34 AM
paulbartrum wrote:
I still have to use Convert, sadly.
Have you tried this?
int v = TypeConverter.ToInt32(argumentValues[0])
I don't know how Convert.ToByte is implemented, but I think TypeConverter.ToInt32 will be faster because it only has to deal with a limited number of types.
Yes, it wasn't much faster, but a bit.

Thanks, I didn't think certain things were so easy to do! :)
Jan 29 at 4:37 PM

Paul: adding caching to PopulateFunctions would probably make for a significant speedup (and easy to implement). Simply, the first time create a dict of all properties found using reflection, then cache them, and use the cached result thereafter.

Coordinator
Jan 30 at 1:38 AM
Yeah, absolutely, this definitely seems like it would be worth doing.