Am I abusing the built-in modifier?

I worked on polishing my JSON code for my ECMAScript runtime, and I decided to run an experiment. The following str function has 4 logical steps, which I split into functions and marked them inline.

 and private str (state:StringifyState) (key:string) (holder:IObject) : IDynamic = let inline fourth (value:IDynamic) = match value.TypeCode with | LanguageTypeCode.Null -> state.environment.CreateString "null" :> IDynamic | LanguageTypeCode.Boolean -> let v = value :?> IBoolean state.environment.CreateString (if v.BaseValue then "true" else "false") :> IDynamic | LanguageTypeCode.String -> let v = value :?> IString state.environment.CreateString (quote v.BaseValue) :> IDynamic | LanguageTypeCode.Number -> let v = value :?> INumber if not (Double.IsInfinity(v.BaseValue)) then v.ConvertToString() :> IDynamic else state.environment.CreateString "null" :> IDynamic | LanguageTypeCode.Object -> let v = value :?> IObject let v = if v.Class = "Array" then ja state v else jo state v state.environment.CreateString v :> IDynamic | _ -> state.environment.Undefined :> IDynamic let inline third (value:IDynamic) = match value.TypeCode with | LanguageTypeCode.Object -> let v = value :?> IObject match v.Class with | "Number" -> fourth (v.ConvertToNumber()) | "String" -> fourth (v.ConvertToString()) | "Boolean" -> fourth (v.ConvertToBoolean()) | _ -> fourth value | _ -> fourth value let inline second (value:IDynamic) = match state.replacerFunction with | :? ICallable as f -> let args = state.environment.CreateArgs ([| state.environment.CreateString key :> IDynamic; value |]) let value = f.Call (state.environment, holder :> IDynamic, args) third value | _ -> third value let inline first (value:IDynamic) = match value with | :? IObject as v -> let toJSON = v.Get "toJSON" match toJSON with | :? ICallable as f -> let args = state.environment.CreateArgs ([| state.environment.CreateString key :> IDynamic |]) let value = f.Call (state.environment, value, args) second value | _ -> second value | _ -> second value first (holder.Get key) 

I compiled with full optimization and opened the resulting assembly with Reflector to see the results.

 [CompilationArgumentCounts(new int[] { 1, 1, 1 })] internal static IDynamic str(StringifyState state, string key, IObject holder) { IObject obj3; ICallable callable; ICallable callable2; IArgs args; IDynamic dynamic3; IDynamic dynamic4; ICallable callable3; IDynamic dynamic5; IBoolean flag; IString str; INumber number; IObject obj4; string str2; INumber number2; IObject obj5; string str3; IString str4; IBoolean flag2; IDynamic thisBinding = holder.Get(key); IObject obj2 = thisBinding as IObject; if (obj2 == null) { callable = state.replacerFunction@ as ICallable; if (callable == null) { switch (thisBinding.TypeCode) { case LanguageTypeCode.Object: obj3 = (IObject) thisBinding; str2 = obj3.Class; if (!string.Equals(str2, "Number")) { if (string.Equals(str2, "String")) { dynamic3 = obj3.ConvertToString(); switch (dynamic3.TypeCode) { case LanguageTypeCode.Null: return (IDynamic) state.environment@.CreateString ("null"); case LanguageTypeCode.Boolean: flag = (IBoolean) dynamic3; return (IDynamic) state.environment@.CreateString (!flag.BaseValue ? "false" : "true"); case LanguageTypeCode.String: str4 = (IString) dynamic3; return (IDynamic) state.environment@.CreateString (quote(str4.BaseValue)); case LanguageTypeCode.Number: number = (INumber) dynamic3; if (double.IsInfinity(number.BaseValue)) { return (IDynamic) state.environment@.CreateString ("null"); } return (IDynamic) number.ConvertToString(); // ... I removed a large amount of code. return (IDynamic) state.environment@.Undefined ; } 

Clearly, the inline modifier is quite literal. The code is quite large and some preliminary tests are very efficient. You might consider throwing inline for all of their functions if they do not care about the size of the assemblies received. What are some guidelines I can follow to find out if using inline is appropriate? If possible, I would like to avoid measuring performance every time to determine this.

+4
source share
2 answers

If you use inline solely for performance reasons, I think that all the typical recommendations are related to performance. The most important thing is to set a target performance and profile the application for hot spots. Then use inline if you have reason to believe that this will improve performance and verify that this happens. Keep in mind that the IL generated by the F # compiler is still compiled by JIT, so small small functions (in terms of IL size) can be built into compilation in machine code, even if you do not use inline in F # code.

I usually use inline when I want to use statically permitted type variables (e.g. due to member constraints).

+4
source

I agree with the answer of kvb, but here are two specific reasons:

consider throwing inline on all of their functions if they don't care about the size of the resulting assemblies.

  • The obvious case is that nesting anonymous functions will not work.

  • Additional inlay (especially of large functions) β†’ less (efficient) code fits into the cache β†’ the program runs more slowly.

+2
source

Source: https://habr.com/ru/post/1333534/


All Articles