Я злоупотребляю встроенным модификатором? - PullRequest
4 голосов
/ 28 декабря 2010

Я работал над полировкой своего кода JSON для своей среды выполнения ECMAScript и решил провести эксперимент. Следующая функция str имеет 4 логических шага, которые я разбил на функции и пометил их как встроенные.

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)

Я скомпилировал с полной оптимизацией и открыл получившуюся сборку с помощью Reflector, чтобы увидеть результаты.

[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;
}

Очевидно, модификатор inline довольно буквален. Код довольно большой и с некоторыми предварительными тестами очень эффективен. Можно было бы добавить встроенные функции для всех их функций, если их не заботит размер получающихся сборок. Каким руководствам я могу следовать, чтобы знать, когда использование inline подходит? Если возможно, я бы не хотел измерять производительность каждый раз, чтобы определить это.

Ответы [ 2 ]

4 голосов
/ 28 декабря 2010

Если вы используете inline исключительно из соображений производительности, то я думаю, что применимы все типичные рекомендации, связанные с производительностью. Самое главное, установить целевой показатель производительности и профилировать ваше приложение для горячих точек. Затем используйте inline, если у вас есть основания полагать, что это улучшит производительность, и протестируйте, чтобы убедиться, что это так. Помните, что IL, который генерирует компилятор F #, в любом случае компилируется JIT, поэтому небольшие функции (с точки зрения размера IL) могут быть встроены в компиляцию для машинного кода, даже если вы не используете inline в своем коде F #.

Обычно я использую inline только тогда, когда хочу использовать статически разрешенные переменные типа (например, из-за ограничений членов).

2 голосов
/ 28 декабря 2010

Я согласен с ответом kvb, но здесь есть две конкретные причины, по которым

не стоит рассматривать встроенные функции для всех их функций, если они не заботятся о размере получающихся сборок.

  1. Очевидный случай: встроенные анонимные функции работать не будут.

  2. Больше вставок (особенно больших функций) -> меньше (эффективно) код помещается в кеш -> программа работает медленнее.

...