如何铸就的通用类型T的值增加一倍没有拳击? [英] How to cast a value of generic type T to double without boxing?

查看:221
本文介绍了如何铸就的通用类型T的值增加一倍没有拳击?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下下面这个简单的code:

Imagine the following simple code:

public void F<T>(IList<T> values) where T : struct
{
  foreach (T value in values)
  {
    double result;
    if (TryConvertToDouble((object)value, out result))
    {
      ConsumeValue(result);
    }
  }
}

public void ConsumeValue(double value)
{
}

与上面的code的问题是铸造对象,这会导致拳击循环。

The problem with the above code is casting to object, which results in boxing in the loop.

有没有一种方法来实现相同的功能,即所有的值喂养ConsumeValue而不是诉诸于foreach循环拳击? 注意,使得f必须是一个通用的方法

Is there a way to achieve the same functionality, i.e. feeding ConsumeValue with all the values without resorting to boxing in the foreach loop? Note, that F must be a generic method.

我可以与昂贵的preparation code生活,只要它是在循环外只执行一次。例如,如果一个奇特的动态方法需要被发射,那么它是好的,如果做一次。

I can live with an expensive preparation code as long as it is executed outside the loop just once. For instance, if a fancy dynamic method needs to be emitted, then it is fine if done just once.

修改

T被保证是一些数字型或布尔的。

T is guaranteed to be of some numeric type or bool.

动机。想象的元数据驱动的应用程序,其中一个代理报告的数据流,其中,数据项类型基于所述数据流的元数据被动态地发射。也想象,有规范化器引擎,它知道按照某种算法标准化数字数据流。传入的​​数字数据流的类型是已知的唯一的在运行时,可以针对该数据类型的一个通用方法。正规化,但是预计双打和生产双打。这是一个非常高的水平说明,请不要深入研究了。

Motivation. Imagine meta data driven application, where an agent reports a data stream, where data item type is dynamically emitted based on the data stream meta data. Imagine also, that there is normalizer engine, which knows to normalize numeric data streams according to some algorithm. The type of the incoming numeric data stream is known only at run time and can be directed to a generic method of that data type. The normalizer, however, expects doubles and produces doubles. This is a very high level description, please do not delve into it.

EDIT2

就铸造翻番。其实我们有一个方法来转换为加倍具有以下签名:

Concerning the cast to double. Actually we have a method to convert to double with the following signature:

bool TryConvertToDouble(object value, out double result);

我已经用它的例子摆在首位,但我想,以节省空间和书面的东西是行不通的。现在修好了。感谢您的关注。

I should have used it in the example in the first place, but I wanted to save space and written something that is not going to work. Fixed it now. Thanks for noting.

EDIT3

大家好,目前的实现不中的值。而且,即使我没有探查的判决,以它的(如果有的话)性能损失,还是我很想知道是否有没有拳击的解决方案(无需转换为字符串)。我把它称为纯粹的学术兴趣。这真使我感兴趣,因为这样的事情是微不足道的在C ++中有模板,但是,当然,我不是从头开始什么是更好的.NET泛型或C ++模板又愚蠢和无谓的争论。请忽略这最后一句话。

Guys, the current implementation does box the values. And even if I do not have the profiler's verdict as to performance penalty of it (if any), still I am interesting to know whether there is a solution without boxing (and without converting to string). Let me call it purely academic interest. This really interests me, because things like that are trivial in C++ with templates, but, of course, I am not starting yet another stupid and pointless argument over what is better .NET generics or C++ templates. Please, ignore this last sentence.

EDIT4

感谢<一href="http://stackoverflow.com/users/267/lasse-v-karlsen">http://stackoverflow.com/users/267/lasse-v-karlsen谁提供了答案。其实,我已经使用了code样写一个简单的类是这样的:

Thanks to http://stackoverflow.com/users/267/lasse-v-karlsen who provided the answer. Actually, I have used his code sample to write a simple class like this:

public static class Utils<T>
{
  private static class ToDoubleConverterHolder
  {
    internal static Func<T, double> Value = EmitConverter();

    private static Func<T, double> EmitConverter()
    {
      ThrowIfNotConvertableToDouble(typeof(T));

      var method = new DynamicMethod(string.Empty, typeof(double), TypeArray<T>.Value);
      var il = method.GetILGenerator();

      il.Emit(OpCodes.Ldarg_0);
      if (typeof(T) != typeof(double))
      {
        il.Emit(OpCodes.Conv_R8);
      }
      il.Emit(OpCodes.Ret);

      return (Func<T, double>)method.CreateDelegate(typeof(Func<T, double>));
    }
  }

  public static double ConvertToDouble(T value)
  {
    return ToDoubleConverterHolder.Value(value);
  }
}

其中:

  • ThrowIfNotConvertableToDouble(类型)是一个简单的方法,该方法可以确保给定的类型可以转换为加倍,即一些数值类型或布尔。
  • TypeArray是一个辅助类生成新的[] {typeof运算(T)}

该Utils.ConvertToDouble方法将任何数值翻一番最有效率的方式,由这个问题的答案显示。

The Utils.ConvertToDouble method converts any numeric value to double in the most efficient way, shown by the answer to this question.

它的工作原理就像一个魅力 - 感谢的人

It works like a charm - thanks man.

推荐答案

注:在我最初的code基于实例的code产生的错误。请重新检查下面的code。更改后的部分负荷值压入堆栈(即在.Emit行)的顺序。无论是code的答案,仓库已得到修复。

如果你想要去code一代人的路线,因为你提示你的问题,这里的样品code:

If you want to go the route of code generation, as you hint to in your question, here's sample code:

据执行ConsumeValue(它没有在我的例子),1000万次,int数组和布尔值的阵列上,定时执行(它运行的所有code一次,从歪斜的消除JIT开销时机。)

It executes ConsumeValue (which does nothing in my example) 10 million times, on an array of ints and an array of booleans, timing the execution (it runs all the code once, to remove JIT overhead from skewing the timing.)

输出:

F1 ints = 445ms         <-- uses Convert.ToDouble
F1 bools = 351ms
F2 ints = 159ms         <-- generates code on each call
F2 bools = 167ms
F3 ints = 158ms         <-- caches generated code between calls
F3 bools = 163ms

与code一代少大约65%的开销。

Roughly 65% less overhead with code generation.

在code是从我的Mercurial库的位置: HTTP://hg.vkarlsen 。无/ hgweb.cgi /计算器,寻找您SO题号浏览它。

The code is available from my Mercurial repository here: http://hg.vkarlsen.no/hgweb.cgi/StackOverflow, browse it by finding your SO question number.

在code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication15
{
    class Program
    {
        public static void F1<T>(IList<T> values) where T : struct
        {
            foreach (T value in values)
                ConsumeValue(Convert.ToDouble(value));
        }

        public static Action<T> GenerateAction<T>()
        {
            DynamicMethod method = new DynamicMethod(
                "action", MethodAttributes.Public | MethodAttributes.Static,
                CallingConventions.Standard,
                typeof(void), new Type[] { typeof(T) }, typeof(Program).Module,
                false);
            ILGenerator il = method.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0); // get value passed to action
            il.Emit(OpCodes.Conv_R8);
            il.Emit(OpCodes.Call, typeof(Program).GetMethod("ConsumeValue"));
            il.Emit(OpCodes.Ret);

            return (Action<T>)method.CreateDelegate(typeof(Action<T>));
        }

        public static void F2<T>(IList<T> values) where T : struct
        {
            Action<T> action = GenerateAction<T>();
            foreach (T value in values)
                action(value);
        }

        private static Dictionary<Type, object> _Actions =
            new Dictionary<Type, object>();
        public static void F3<T>(IList<T> values) where T : struct
        {
            Object actionObject;
            if (!_Actions.TryGetValue(typeof(T), out actionObject))
            {
                actionObject = GenerateAction<T>();
                _Actions[typeof (T)] = actionObject;
            }
            Action<T> action = (Action<T>)actionObject;
            foreach (T value in values)
                action(value);
        }

        public static void ConsumeValue(double value)
        {
        }

        static void Main(string[] args)
        {
            Stopwatch sw = new Stopwatch();

            int[] ints = Enumerable.Range(1, 10000000).ToArray();
            bool[] bools = ints.Select(i => i % 2 == 0).ToArray();

            for (int pass = 1; pass <= 2; pass++)
            {
                sw.Reset();
                sw.Start();
                F1(ints);
                sw.Stop();
                if (pass == 2)
                    Console.Out.WriteLine("F1 ints = "
                        + sw.ElapsedMilliseconds + "ms");

                sw.Reset();
                sw.Start();
                F1(bools);
                sw.Stop();
                if (pass == 2)
                    Console.Out.WriteLine("F1 bools = "
                        + sw.ElapsedMilliseconds + "ms");

                sw.Reset();
                sw.Start();
                F2(ints);
                sw.Stop();
                if (pass == 2)
                    Console.Out.WriteLine("F2 ints = "
                        + sw.ElapsedMilliseconds + "ms");

                sw.Reset();
                sw.Start();
                F2(bools);
                sw.Stop();
                if (pass == 2)
                    Console.Out.WriteLine("F2 bools = "
                        + sw.ElapsedMilliseconds + "ms");

                sw.Reset();
                sw.Start();
                F3(ints);
                sw.Stop();
                if (pass == 2)
                    Console.Out.WriteLine("F3 ints = "
                        + sw.ElapsedMilliseconds + "ms");

                sw.Reset();
                sw.Start();
                F3(bools);
                sw.Stop();
                if (pass == 2)
                    Console.Out.WriteLine("F3 bools = "
                        + sw.ElapsedMilliseconds + "ms");
            }
        }
    }
}

请注意,如果你让GenerationAction,F2 / 3和ConsumeValue非静态,你必须改变code稍微:

Note that if you make GenerationAction, F2/3 and ConsumeValue non-static, you have to change the code slightly:

  1. 所有动作&LT; T&GT; 的声明变得动作&lt;程序,T&GT;
  2. 更​​改创建DynamicMethod的对包括这个参数:

  1. All Action<T> declarations becomes Action<Program, T>
  2. Change the creation of the DynamicMethod to include the "this" parameter:

DynamicMethod method = new DynamicMethod(
    "action", MethodAttributes.Public | MethodAttributes.Static,
    CallingConventions.Standard,
    typeof(void), new Type[] { typeof(Program), typeof(T) },
    typeof(Program).Module,
    false);

  • 修改说明在合适的时间加载正确的价值观:

  • Change the instructions to load the right values at the right times:

    il.Emit(OpCodes.Ldarg_0); // get "this"
    il.Emit(OpCodes.Ldarg_1); // get value passed to action
    il.Emit(OpCodes.Conv_R8);
    il.Emit(OpCodes.Call, typeof(Program).GetMethod("ConsumeValue"));
    il.Emit(OpCodes.Ret);
    

  • 通这个每当它被称为动作:

  • Pass "this" to the action whenever it is called:

    action(this, value);
    

  • 下面是完整的修改方案,非静态方法:

    Here's the complete changed program for non-static methods:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    
    namespace ConsoleApplication15
    {
        class Program
        {
            public void F1<T>(IList<T> values) where T : struct
            {
                foreach (T value in values)
                    ConsumeValue(Convert.ToDouble(value));
            }
    
            public Action<Program, T> GenerateAction<T>()
            {
                DynamicMethod method = new DynamicMethod(
                    "action", MethodAttributes.Public | MethodAttributes.Static,
                    CallingConventions.Standard,
                    typeof(void), new Type[] { typeof(Program), typeof(T) },
                    typeof(Program).Module,
                    false);
                ILGenerator il = method.GetILGenerator();
    
                il.Emit(OpCodes.Ldarg_0); // get "this"
                il.Emit(OpCodes.Ldarg_1); // get value passed to action
                il.Emit(OpCodes.Conv_R8);
                il.Emit(OpCodes.Call, typeof(Program).GetMethod("ConsumeValue"));
                il.Emit(OpCodes.Ret);
    
                return (Action<Program, T>)method.CreateDelegate(
                    typeof(Action<Program, T>));
            }
    
            public void F2<T>(IList<T> values) where T : struct
            {
                Action<Program, T> action = GenerateAction<T>();
                foreach (T value in values)
                    action(this, value);
            }
    
            private static Dictionary<Type, object> _Actions =
                new Dictionary<Type, object>();
            public void F3<T>(IList<T> values) where T : struct
            {
                Object actionObject;
                if (!_Actions.TryGetValue(typeof(T), out actionObject))
                {
                    actionObject = GenerateAction<T>();
                    _Actions[typeof (T)] = actionObject;
                }
                Action<Program, T> action = (Action<Program, T>)actionObject;
                foreach (T value in values)
                    action(this, value);
            }
    
            public void ConsumeValue(double value)
            {
            }
    
            static void Main(string[] args)
            {
                Stopwatch sw = new Stopwatch();
    
                Program p = new Program();
                int[] ints = Enumerable.Range(1, 10000000).ToArray();
                bool[] bools = ints.Select(i => i % 2 == 0).ToArray();
    
                for (int pass = 1; pass <= 2; pass++)
                {
                    sw.Reset();
                    sw.Start();
                    p.F1(ints);
                    sw.Stop();
                    if (pass == 2)
                        Console.Out.WriteLine("F1 ints = "
                            + sw.ElapsedMilliseconds + "ms");
    
                    sw.Reset();
                    sw.Start();
                    p.F1(bools);
                    sw.Stop();
                    if (pass == 2)
                        Console.Out.WriteLine("F1 bools = "
                            + sw.ElapsedMilliseconds + "ms");
    
                    sw.Reset();
                    sw.Start();
                    p.F2(ints);
                    sw.Stop();
                    if (pass == 2)
                        Console.Out.WriteLine("F2 ints = "
                            + sw.ElapsedMilliseconds + "ms");
    
                    sw.Reset();
                    sw.Start();
                    p.F2(bools);
                    sw.Stop();
                    if (pass == 2)
                        Console.Out.WriteLine("F2 bools = "
                            + sw.ElapsedMilliseconds + "ms");
    
                    sw.Reset();
                    sw.Start();
                    p.F3(ints);
                    sw.Stop();
                    if (pass == 2)
                        Console.Out.WriteLine("F3 ints = "
                            + sw.ElapsedMilliseconds + "ms");
    
                    sw.Reset();
                    sw.Start();
                    p.F3(bools);
                    sw.Stop();
                    if (pass == 2)
                        Console.Out.WriteLine("F3 bools = "
                            + sw.ElapsedMilliseconds + "ms");
                }
            }
        }
    }
    

    这篇关于如何铸就的通用类型T的值增加一倍没有拳击?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆