Wednesday, June 17, 2020

CSharp - Speed comparison of Modulo implementations including Russian Peasant


If for some odd reason you need to replace the built-in modulo operator, here are a few implementations with speed timings.  


Of particular interest the bit-wise shifting Russian Peasant implementation of modulo.
In C language, Russian Peasant does beat the native mod operand, especially the inline assembly code version.


Update: In my next post I added another implementation called simple additive method.

However, in C# you be hard pressed to beat the native implementation.


using System;
using System.Diagnostics; 
					
public class Program
{
	public static int ModuloBitShiftRussianPeasant(int i, int j)
    {
		if (i <= j) //trivial modulo
			return (i == j) ? 0 : i;
		else if (j==0)
			return i;

		int x = j;
		int halfi = i >> 1;
		while (x < halfi)
		{
			x <<= 1;
		}
		while (i >= j)
		{
			if (i >= x)
				i -= x;
			else
				x >>= 1;
		}
		return i;
	}
	
	public static int ModuloDecimalMath(int i, int j)
    {
		if (i <= j) //trivial modulo
			return (i == j) ? 0 : i;
		else if (j==0)
			return i;
		
		decimal decresult = i/(decimal)j; 
		//Console.WriteLine("decresult="+decresult.ToString());
		//Console.WriteLine("denominator="+(decresult - (int)decresult ).ToString()); 
		//Console.WriteLine("modulo="+((decresult - (int)decresult)*j).ToString()); 
		//modulo=3.999999999999999999999999997 issue! 
		           
		//decimal modulo = (decresult - (int)decresult)*j; 		
		
		return (int)(((decresult - (int)decresult)*j)+0.000000000000000000000000003M); //add correction
	}
	
	public static int ModuloMathString(int i, int j)
    {
		if (i <= j) //trivial modulo
			return (i == j) ? 0 : i;
		else if (j==0)
			return i;
		
		string decresult = (i/(decimal)j).ToString();
		int idxperiod = decresult.IndexOf('.'); 
		if (idxperiod > -1 )
		{
			string denominator = decresult.Substring(idxperiod); 
			//Console.WriteLine("denominator="+Convert.ToDecimal(denominator).ToString());   	 
			i = (int)(j*(Convert.ToDecimal(denominator)+0.000000000000000000000000003M)); 
		}
		else 
			i = Convert.ToInt32(decresult); 
	
		return i;
	}
	
	public static int BasicModuloMathString(int i, int j) 
	{
		return i % j; 
	}
	
	
	public static void Main()
	{
		int number = 123; 
		int div = 7; 
		int mod = 0; 
		Stopwatch sw = new Stopwatch(); 
		sw.Start(); 
		mod = BasicModuloMathString(number, div); 
		sw.Stop();
		
		Console.WriteLine("{0} % {1} mod={2}",number,div,mod);
		Console.WriteLine(sw.ElapsedTicks+" ticks");
		
		sw.Reset();
		sw.Start(); 
		mod = ModuloBitShiftRussianPeasant(number, div); 
		sw.Stop();
		
		Console.WriteLine("ModuloBitShiftRussianPeasant mod="+mod);
		Console.WriteLine(sw.ElapsedTicks+" ticks");
		
		
		sw.Reset();
		sw.Start(); 
		mod = ModuloDecimalMath(number, div); 
		sw.Stop();
		
		Console.WriteLine("ModuloDecimalMath mod="+mod);
		Console.WriteLine(sw.ElapsedTicks+" ticks");
		
		sw.Reset();
		sw.Start(); 
		mod = ModuloMathString(number, div); 
		sw.Stop();
		
		Console.WriteLine("ModuloMathString mod="+mod);
		Console.WriteLine(sw.ElapsedTicks+" ticks");
		
	}
}

No comments: