I've been doing some playing around and have written some code for
mathematical operations. Among those operations is something called a
box approximation for the integral  of a function
( http://en.wikipedia.org/wiki/Rectangle_Rule ). 

At the behest of mhutch in #mono, I tried a few things to improve the
speed of the method. The original speed was approximately 385 ms for
10,000,000 iterations of the midpoint box approximation over the
function x^3. This included 4 multiplies, 3 adds, and 1 function call
for each run of the loop, plus a one-time cost of a divide.

The things attempted to improve speed were: inlining the function, and
removing a switch() that determined if the approximation was to be
midpoint, left, or right.

Here are the results from those tests:

Iteration 1
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, no inline, w/ branch: 469
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, inlined, w/ branch: 132
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, no inline, no branch: 235
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, inlined, no branch: 250
....(Much of the same here)....
Iteration 10
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, no inline, w/ branch: 456
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, inlined, w/ branch: 130
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, no inline, no branch: 228
        Integral of x^3 over [0, 5]: 156.250062500004
        Box approximation, inlined, no branch: 245

The thing that immediately struck me as odd was: how is the inlined
BoxApproximation that HAS a switch() call to check whether it's doing
left, right or midpoint faster than everything else. It's not just a
little faster, it's a lot faster. The machine running all of these tests
is running Mono trunk as of a few days ago, x86 processor with a 2.0 GHz
clock speed and 512 MB of RAM (it's a VMware server virtual machine).
Running the same thing on Mono 1.9.1 outside of VMware yields similar
results.

My question is--why is that method the fastest? Attached are compileable
versions of the Main() method and the class that holds the function
definitions.

Thanks,
Bojan Rajkovic
// Functions.cs
//
// Copyright (c) 2008 Bojan Rajkovic
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

using System;

namespace MathPlay
{
	public enum ApproximationType
	{
		Left,
		Right,
		Midpoint
	}
	
	static class Function
	{
		public static double Limit(double accuracy, long step, Func<long, double> f)
		{
			double lastResult, nextResult;
			long n = 0;
			lastResult = f(n);
			if (double.IsNaN(lastResult)) lastResult = 0;
			else if (double.IsInfinity(lastResult)) lastResult = 0;
			long iterations = 0;
			while (true) {
				n += step;
				nextResult = f(n);
				double differential = Math.Abs(nextResult - lastResult);
				if (differential <= accuracy) {
					return nextResult;
				} else {
					lastResult = nextResult;
					iterations++;
				}
			}
		}
	
		public static class Derivative
		{
			public static double Numeric(double startingApproximation, double minimumApproximation, double x, Func<double, double> f)
			{
				if (startingApproximation <= minimumApproximation)
					return f(x);
				double approximation = startingApproximation;
				double value = f(x);
				while (approximation > minimumApproximation) {
					value = (f(x + approximation) - f(x - approximation))/(2*approximation);
					approximation /= 2;
				}
				return value;
			}
		}
		
		public static class Integral
		{
			public static double BoxApproximation(double start, double end, long steps, Func<double, double> f, ApproximationType type)
			{
				double delta = (end - start)/steps;
				double approximationFactor = 0;
				double result = 0;
		
				switch (type) {
					case ApproximationType.Left:
						approximationFactor = 1;
						break;
					case ApproximationType.Midpoint:
						approximationFactor = .5;
						break;
					case ApproximationType.Right:
						approximationFactor = 0;
						break;
					default:
						approximationFactor = 0;
						break;
				}
							
				for (int i = 0; i <= steps; i++)
					result += f(start + (i + approximationFactor)*delta) * delta;
		
				return result;
			}
			
			public static double MidpointApproximation(double start, double end, long steps, Func<double, double> f)
			{
				double delta = (end - start)/steps;
				double result = 0;
							
				for (int i = 0; i <= steps; i++)
					result += f(start + (i + 0.5)*delta) * delta;
		
				return result;
			}		
			
			public static double MidpointApproximationInline(double start, double end, long steps)
			{
				double delta = (end - start)/steps;
				double result = 0;
				double x;
							
				for (int i = 0; i <= steps; i++) {
					x = (start + (i + 0.5)*delta);
					result += x*x*x*delta;
				}
		
				return result;
			}
			
			public static double BoxApproximationInline(double start, double end, long steps, ApproximationType type)
			{
				double delta = (end - start)/steps;
				double approximationFactor = 0;
				double result = 0;
				double x;
		
				switch (type) {
					case ApproximationType.Left:
						approximationFactor = 1;
						break;
					case ApproximationType.Midpoint:
						approximationFactor = .5;
						break;
					case ApproximationType.Right:
						approximationFactor = 0;
						break;
					default:
						approximationFactor = 0;
						break;
				}
								
				for (int i = 0; i <= steps; i++) {
					x = (start + (i + approximationFactor)*delta);
					result += x*x*x*delta;
				}
		
				return result;
			}
				
	
			public static double SimpsonApproximation(double start, double end, double accuracy, Func<double, double> f)
			{ 	
				double h = end - start;
				double s2 = 1;
				double s = f(start) + f(end);
				double s1, s3, x;
				int iterations = 0;
		
				do {
					s3 = s2;
					h /= 2;
					s1 = 0;
					x = start + h;
					do {
						s1 += 2*f(x);
						x += 2*h;
					} while (x < end);
		
					s += s1;
					s2 = (s + s1)*h/3;
					x = Math.Abs(s3 - s2)/15;
					iterations++;
				} while ( x > accuracy);
		
				return s2;
			}
		}
	}
}
// MathPlay.cs
//
// Copyright (c) 2008 Bojan Rajkovic
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

using System;
using System.Diagnostics;
using MathPlay;

public static class TestMath
{
	public static void Main()
	{
		Func<double, double> g = x => x*x*x;
		for (int i = 1; i < 11; i++) {
			Console.WriteLine("Iteration {0}", i);
			Stopwatch sw = new Stopwatch();
			sw.Start();
			Console.WriteLine("\tIntegral of x^3 over [0, 5]: {0}",
				Function.Integral.BoxApproximation(0, 5, 10000000, g, ApproximationType.Midpoint));
			sw.Stop();
			Console.WriteLine("\tBox approximation, no inline, w/ branch: {0}", sw.ElapsedMilliseconds);
			sw.Reset();
			sw.Start();
			Console.WriteLine("\tIntegral of x^3 over [0, 5]: {0}",
				Function.Integral.BoxApproximationInline(0, 5, 10000000, ApproximationType.Midpoint));
			sw.Stop();
			Console.WriteLine("\tBox approximation, inlined, w/ branch: {0}", sw.ElapsedMilliseconds);
			sw.Reset();
			sw.Start();
			Console.WriteLine("\tIntegral of x^3 over [0, 5]: {0}",
				Function.Integral.MidpointApproximation(0, 5, 10000000, g));
			sw.Stop();
			Console.WriteLine("\tBox approximation, no inline, no branch: {0}", sw.ElapsedMilliseconds);
			sw.Reset();
			sw.Start();
			Console.WriteLine("\tIntegral of x^3 over [0, 5]: {0}",
				Function.Integral.MidpointApproximationInline(0, 5, 10000000));
			sw.Stop();
			Console.WriteLine("\tBox approximation, inlined, no branch: {0}", sw.ElapsedMilliseconds);
		}
	}
}
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to