为什么常规乘法运算符比BigMul方法更有效?

时间:2015-04-18 20:56:21

标签: c# performance

我搜索了* operatorMath.BigMul method之间的差异,却一无所获。所以我决定尝试相互测试他们的效率。请考虑以下代码:

public class Program
{
    static void Main()
    {

        Stopwatch MulOperatorWatch = new Stopwatch();
        Stopwatch MulMethodWatch = new Stopwatch();

        MulOperatorWatch.Start();
        // Creates a new MulOperatorClass to perform the start method 100 times.
        for (int i = 0; i < 100; i++)
        {
            MulOperatorClass mOperator = new MulOperatorClass();
            mOperator.start();

        }
        MulOperatorWatch.Stop();

        MulMethodWatch.Start();
        for (int i = 0; i < 100; i++)
        {
            MulMethodClass mMethod = new MulMethodClass();
            mMethod.start();
        }
        MulMethodWatch.Stop();

        Console.WriteLine("Operator = " + MulOperatorWatch.ElapsedMilliseconds.ToString());
        Console.WriteLine("Method = " + MulMethodWatch.ElapsedMilliseconds.ToString());
        Console.ReadLine();
    }

    public class MulOperatorClass
    {
        public void start()
        {
            List<long> MulOperatorList = new List<long>();
            for (int i = 0; i < 15000000; i++) 
            {
                MulOperatorList.Add(i * i);
            }
        }
    }

    public class MulMethodClass
    {
        public void start()
        {
            List<long> MulMethodList = new List<long>();
            for (int i = 0; i < 15000000; i++)
            {
                MulMethodList.Add(Math.BigMul(i,i));
            }
        }
    }
}

总结一下:我创建了两个类 - MulMethodClassMulOperatorClass,它们同时执行start方法,该方法填充了List<long类型的变量。值i multiply by i多次。这些方法之间的唯一区别是在运算符类中使用* operator,以及在方法类中使用Math.BigMul

我正在创建每个类的100个实例,只是为了防止和溢出列表(我无法创建1000000000个项目列表)。

然后,我测量100个类中每个类执行所需的时间。结果很奇怪:我做了大约15次这个过程,平均结果是(以毫秒为单位):

  

运营商= 20357

     

方法= 24579

大约4.5秒的差异,我觉得很多。我查看了BigMul method的源代码 - 它使用了* operator,实际上也做了同样的事情。

所以,对于我的问题:

  • 为什么这种方法存在?它完全一样。
  • 如果它完全相同,为什么这两者之间存在巨大的效率差异?

我只是好奇:)

1 个答案:

答案 0 :(得分:2)

微观标记是艺术。你是对的,x86上的方法慢了大约10%。 x64上的速度相同。请注意,您必须将两个长整数乘以((long)i) * ((long)i),因为它是BigMul

现在,如果你想要microbenchmark,一些简单的规则:

A)不要在基准测试代码中分配内存......你不希望GC运行(你正在扩大List<>

B)在定时区域之外预分配内存(在运行代码之前创建具有正确容量的List<>

C)在对基准测试之前至少运行一次或两次方法。

D)尝试不做任何事情,除了你的基准测试,但强迫编译器运行你的代码。例如,基于操作的结果检查始终为真的条件,如果为假则抛出异常通常足以欺骗编译器。

static void Main()
{
    // Check x86 or x64
    Console.WriteLine(IntPtr.Size == 4 ? "x86" : "x64");

    // Check Debug/Release
    Console.WriteLine(IsDebug() ? "Debug, USELESS BENCHMARK" : "Release");

    // Check if debugger is attached
    Console.WriteLine(System.Diagnostics.Debugger.IsAttached ? "Debugger attached, USELESS BENCHMARK!" : "Debugger not attached");

    // High priority
    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

    Stopwatch MulOperatorWatch = new Stopwatch();
    Stopwatch MulMethodWatch = new Stopwatch();

    // Prerunning of the benchmarked methods
    MulMethodClass.start();
    MulOperatorClass.start();

    {
        // No useless method allocation here
        MulMethodWatch.Start();

        for (int i = 0; i < 100; i++)
        {
            MulMethodClass.start();
        }

        MulMethodWatch.Stop();
    }

    {
        // No useless method allocation here
        MulOperatorWatch.Start();

        for (int i = 0; i < 100; i++)
        {
            MulOperatorClass.start();
        }

        MulOperatorWatch.Stop();
    }


    Console.WriteLine("Operator = " + MulOperatorWatch.ElapsedMilliseconds.ToString());
    Console.WriteLine("Method = " + MulMethodWatch.ElapsedMilliseconds.ToString());
    Console.ReadLine();
}

public class MulOperatorClass
{
    // The method is static. No useless memory allocation
    public static void start()
    {
        for (int i = 2; i < 15000000; i++)
        {
            // This condition will always be false, but the compiler
            // won't be able to remove the code
            if (((long)i) * ((long)i) == ((long)i))
            {
                throw new Exception();
            }
        }
    }
}

public class MulMethodClass
{
    public static void start()
    {
        // The method is static. No useless memory allocation
        for (int i = 2; i < 15000000; i++)
        {
            // This condition will always be false, but the compiler
            // won't be able to remove the code
            if (Math.BigMul(i, i) == i)
            {
                throw new Exception();
            }
        }
    }
}

private static bool IsDebug()
{
    // Taken from http://stackoverflow.com/questions/2104099/c-sharp-if-then-directives-for-debug-vs-release
    object[] customAttributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(DebuggableAttribute), false);

    if ((customAttributes != null) && (customAttributes.Length == 1))
    {
        DebuggableAttribute attribute = customAttributes[0] as DebuggableAttribute;
        return (attribute.IsJITOptimizerDisabled && attribute.IsJITTrackingEnabled);
    }

    return false;
}

E)如果您确定您的代码没问题,请尝试更改测试的顺序

F)将您的程序置于更高优先级

但要高兴: - )

至少另一个人有同样的问题,写了一篇博客文章:http://reflectivecode.com/2008/10/mathbigmul-exposed/

他做了同样的错误。