Using == or .Equals() for bool comparison - c#

I was reviewing some code, and I found something that looked like this:
public class MyClass
{
public bool IsEditable { get; set; }
public void HandleInput()
{
if (IsEditable.Equals(false))
{
//do stuff
}
}
}
As far as I know, (IsEditable.Equals(false)) is identical to (IsEditable == false) (and also the same as (!IsEditable)).
Besides personal preference, is there any difference at all between .Equals() and ==, specifically when used to compare bools?

This is mostly a readability issue. I'd normally use == because that's what I'm used to looking at.
Specifically with bools, you don't have to compare them at all
if(!IsEditable)
will suffice
although, Sometimes I myself do write things like if (val == false) just to be extra sure that i don't misread it when i have to modify the code.

In fact, for basic types such as int, bool etc. there is a difference between calling Equals() and == due to the fact that the CIL has instructions for handling such types. Calling Equals() forces boxing of the value and making a virtual method call, whereas usage of == leads to usage of a single CIL instruction.
!value and value == false is actually the same, at least in Microsoft's C# compiler bundled with .NET 4.0.
Hence, the comparisons within the following methods
public static int CompareWithBoxingAndVirtualMethodCall(bool value)
{
if (value.Equals(false)) { return 0; } else { return 1; }
}
public static int CompareWithCILInstruction(bool value)
{
if (value == false) { return 0; } else { return 1; }
if (!value) { return 0; } else { return 1; } // comparison same as line above
}
will compile to to the following CIL instructions:
// CompareWithBoxingAndVirtualMethodCall
ldarga.s 'value'
ldc.i4.0
call instance bool [mscorlib]System.Boolean::Equals(bool) // virtual method call
brfalse.s IL_000c // additional boolean comparison, jump for if statement
// CompareWithCILInstruction
ldarg.0
brtrue.s IL_0005 // actual single boolean comparison, jump for if statement

The Equals way appears to be significantly slower - roughly 2.7 times in debug mode, and more than seven times in release mode.
Here is my quick and dirty benchmark:
public static void Main() {
bool a = bool.Parse("false");
bool b = bool.Parse("true");
bool c = bool.Parse("true");
var sw = new Stopwatch();
const int Max = 1000000000;
int count = 0;
sw.Start();
// The loop will increment count Max times; let's measure how long it takes
for (int i = 0; i != Max; i++) {
count++;
}
sw.Stop();
var baseTime = sw.ElapsedMilliseconds;
sw.Start();
count = 0;
for (int i = 0; i != Max; i++) {
if (a.Equals(c)) count++;
if (b.Equals(c)) count++;
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds - baseTime);
sw.Reset();
count = 0;
sw.Start();
for (int i = 0; i != Max; i++) {
if (a==c) count++;
if (b==c) count++;
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds - baseTime);
sw.Reset();
count = 0;
sw.Start();
for (int i = 0; i != Max; i++) {
if (!a) count++;
if (!b) count++;
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds - baseTime);
}
Running this produces the following results:
In debug mode
8959
2950
1874
In release mode
5348
751
7
Equals appears to be the slowest. There appears to be little difference between == and !=. However, if (!boolExpr) appears to be the clear winner.

If you decompile System.Boolean and look at it, It's Equals overloads are defined thus:
public override bool Equals(object obj)
{
if (!(obj is bool))
return false;
else
return this == (bool) obj;
}
public bool Equals(bool obj)
{
return this == obj;
}
I would like to think the C# compiler's optimizer and the .Net JIT compiler would be smart enough to inline these, at least for release/optimized compilations, making them exactly the same.

There is a difference - at least in .NET 4.8 - I believe the reason is because of the boxing described in Oliver Hanappi's answer:
static void Main(string[] args)
{
object lhs = true;
object rhs = true;
Console.WriteLine($"Are Equal - {(lhs == rhs ? "Yes" : "No")}"); // Outputs no
Console.WriteLine($"Are Equal - {(lhs.Equals(rhs) ? "Yes" : "No")}"); // Outputs yes
Console.ReadLine();
}

Take a look at the following quote Taken from here:
The Equals method is just a virtual one defined in System.Object, and
overridden by whichever classes choose to do so. The == operator is an
operator which can be overloaded by classes, but which usually has
identity behaviour.
For reference types where == has not been overloaded, it compares
whether two references refer to the same object - which is exactly
what the implementation of Equals does in System.Object.
So in short, Equals is really just doing a == anyways.

In this case, with bools, it doesn't make any difference, however with other built-in non reference types it can.
== allows for conversions of types if it can .Equals won't

== is always better than .Equals. In the case of integer comparison, == runs faster than .Equals. In the below test, the elapsed time using == 157, while for .Equals the elapsed time is 230.
class Program
{
static void Main(string[] args)
{
Program programObj = new Program();
programObj.mymethod();
programObj.mynextmethod();
}
void mynextmethod()
{
var watch = Stopwatch.StartNew();
for (int i = 0; i < 60000000; i++)
{
int j = 0;
if (i.Equals(j))
j = j + 1;
}
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine("Time take in method" + elapsedMs);
Console.ReadLine();
}
void mymethod()
{
var watch = Stopwatch.StartNew();
for (int i = 0; i < 60000000; i++)
{
int j = 0;
if (i == j)
j = j + 1;
}
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine("Time take in method" + elapsedMs);
}
}

Related

Less-than operator vs Math.Abs() to check for negative value

In the example below, is there any advantage (performance wise) to using the less than ( < ) operator to check if a value is negative compared to using Math.Abs?
int value = 0;
if(value < 0){
// Value is negative
}
if(Math.Abs(value) != value){
// Value is negative
}
looking at the source code of Abs:
public static int Abs(int value) {
if (value >= 0)
return value;
else
return AbsHelper(value);
}
private static int AbsHelper(int value) {
Contract.Requires(value < 0, "AbsHelper should only be called for negative values! (hack for JIT inlining)");
if (value == Int32.MinValue)
throw new OverflowException(Environment.GetResourceString("Overflow_NegateTwosCompNum"));
Contract.EndContractBlock();
return -value;
}
It will not only be slower but will also have different behavior to call Math.Abs instead of < and will throw an exception for value Int32.MinValue (-2147483648)
The less-than operator is faster:
using System;
using System.Diagnostics;
namespace so64610522
{
class Program
{
static bool IsNeg1(int a)
{
return a < 0;
}
static bool IsNeg2(int a)
{
return (Math.Abs(a) != a);
}
static void Main(string[] args)
{
var min = -300000000;
var max = +300000000;
var sw = new Stopwatch();
sw.Start();
for (int i = min; i < max; i++)
{
IsNeg1(i);
}
var t1 = sw.ElapsedMilliseconds;
sw.Restart();
for (int i = min; i < max; i++)
{
IsNeg2(i);
}
var t2 = sw.ElapsedMilliseconds;
Console.WriteLine("{0} vs {1}", t1, t2);
}
}
}
On my machine this outputs
1761 vs 2608
so the less-than operator is ~50% faster than the abs method.
A common way to compare performance is to see how many operations can be achieved per timespan. You could easily write something to compare these two methods (notwithstanding that they give different results!)
public static async Task Main()
{
var value = 100;
var result1 = await Perf(() => {var x = value < 0;});
Console.WriteLine(result1);
var result2 = await Perf(() => {var x = Math.Abs(value) != value;});
Console.WriteLine(result2);
}
static async Task<int> Perf(Action act)
{
int numOps = 0;
var cts = new CancellationTokenSource();
await Task.WhenAny(Task.Delay(2000),Task.Run(() => {
while(true)
{
act();
numOps++;
}
},cts.Token));
cts.Cancel();
return numOps;
}
Live example: https://dotnetfiddle.net/tgascX
The results I got in 2 seconds were:
< = 501726838
Math.Abs = 339073849
Making using < about 48% quicker

Chain linked classes

I have created long chains of classes, with each link(class) knowing only the next and previous link.
I see great advantage over Arrays when the index is not important.
public class ChainLink
{
public ChainLink previousLink, nextLink;
// Accessors here
}
Questions :
What is this technique actually called? (I don't know what to search)
Is there a .Net class that does the same thing?
Is there a noticeable performance impact vs. Array or List?
Example of the Accessors I use
Assign to the chain :
public ChainLink NextLink {
get{ return _nextLink;}
set {
_nextLink = value;
if (value != null)
value._previousLink = this;
}
}
public void InsertNext (ChainLink link)
{
link.NextLink = _nextLink;
link.PreviousLink = this;
}
Shortening the chain :
If I un-assign the next link of a chain, leaving the remaining links un-referenced by the main program, the garbage collector will dispose of the data for me.
Testing circular referencing :
public bool IsCircular ()
{
ChainLink link = this;
while (link != null) {
link = link._nextLink;
if (link == this)
return true;
}
return false;
}
Offset index :
public ChainLink this [int offset] {
get {
if (offset > 0 && _nextLink != null)
return _nextLink [offset - 1];
if (offset < 0 && _previousLink != null)
return _previousLink [offset + 1];
return this;
}
}
1) This structure is called a double linked list
2) This implementation exists in C# through LinkedList
3) There is a lot of articles on this topic :
here or
this SO post
Others have answered the question about what this is called and the equivalent .Net class (a LinkedList), but I thought I'd quickly look at the speed of your ChainLink vs an array and a List.
I added a method Foo() to your ChainLink class so that there was something to access for each object instance:
public class ChainLink
{
public ChainLink previousLink, nextLink;
// Accessors here
public void Foo()
{ }
}
The first method creates an array and then times how long it takes to access each item in the array:
private void TestArray()
{
// Setup the Array
ChainLink[] Test = new ChainLink[1000000];
for (int i = 0; i < 1000000; i++)
Test[i] = new ChainLink();
// Use a Stopwatch to measure time
Stopwatch SW;
SW = new Stopwatch();
SW.Start();
// Go through items in the array
for (int i = 0; i < Test.Length; i++)
Test[i].Foo();
// Stop timer and report results
SW.Stop();
Console.WriteLine(SW.Elapsed);
}
Next, I created a method to use a List<T> and time how long it takes to access each item in it:
private void TestList()
{
// Setup the list
List<ChainLink> Test = new List<ChainLink>();
for (int i = 0; i < 1000000; i++)
Test.Add(new ChainLink());
// Use a Stopwatch to measure time
Stopwatch SW;
SW = new Stopwatch();
SW.Start();
// Go through items in the list
for (int i = 0; i < Test.Count; i++)
Test[i].Foo();
// Stop timer and report results
SW.Stop();
Console.WriteLine(SW.Elapsed);
}
Finally, I created a method to use your ChainLink and move through the next item until there are no more:
private void TestChainLink()
{
// Setup the linked list
ChainLink Test = new ChainLink();
for (int i = 0; i < 1000000; i++)
{
Test.nextLink = new ChainLink();
Test = Test.nextLink;
}
// Use a Stopwatch to measure time
Stopwatch SW;
SW = new Stopwatch();
SW.Start();
// Go through items in the linked list
while (Test != null)
{
Test.Foo();
Test = Test.nextLink;
}
// Stop timer and report results
SW.Stop();
Console.WriteLine(SW.Elapsed);
}
Running each of these yields some revealing results:
TestArray(): 00:00:00.0058576
TestList(): 00:00:00.0103650
TestChainLink(): 00:00:00.0000014
Multiple iterations reveal similar figures.
In conclusion, your ChainLink is about 4,100 times faster than an array, and about 7,400 times faster than a List.

Why is the regular multiplication operator is much more efficient than the BigMul method?

I've searched for the differences between the * operator and the Math.BigMul method, and found nothing. So I've decided I would try and test their efficiency against each other. Consider the following code :
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));
}
}
}
}
To sum it up : I've created two classes - MulMethodClass and MulOperatorClass that performs both the start method, which fills a varible of type List<long with the values of i multiply by i many times. The only difference between these methods are the use of the * operator in the operator class, and the use of the Math.BigMul in the method class.
I'm creating 100 instances of each of these classes, just to prevent and overflow of the lists (I can't create a 1000000000 items list).
I then measure the time it takes for each of the 100 classes to execute. The results are pretty peculiar : I've did this process about 15 times and the average results were (in milliseconds) :
Operator = 20357
Method = 24579
That about 4.5 seconds difference, which I think is a lot. I've looked at the source code of the BigMul method - it uses the * operator, and practically does the same exact thing.
So, for my quesitons :
Why such method even exist? It does exactly the same thing.
If it does exactly the same thing, why there is a huge efficiency difference between these two?
I'm just curious :)
Microbenchmarking is art. You are right the method is around 10% slower on x86. Same speed on x64. Note that you have to multiply two longs, so ((long)i) * ((long)i), because it is BigMul!
Now, some easy rules if you want to microbenchmark:
A) Don't allocate memory in the benchmarked code... You don't want the GC to run (you are enlarging the List<>)
B) Preallocate the memory outside the timed zone (create the List<> with the right capacity before running the code)
C) Run at least once or twice the methods before benchmarking it.
D) Try to not do anything but what you are benchmarking, but to force the compiler to run your code. For example checking for an always true condition based on the result of the operation, and throwing an exception if it is false is normally good enough to fool the compiler.
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) If you are really sure your code is ok, try changing the order of the tests
F) Put your program in higher priority
but be happy :-)
at least another persons had the same question, and wrote a blog article: http://reflectivecode.com/2008/10/mathbigmul-exposed/
He did the same errors you did.

Using properties and performance

I was optimizing my code, and I noticed that using properties (even auto properties) has a profound impact on the execution time. See the example below:
[Test]
public void GetterVsField()
{
PropertyTest propertyTest = new PropertyTest();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
propertyTest.LoopUsingCopy();
Console.WriteLine("Using copy: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingGetter();
Console.WriteLine("Using getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingField();
Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
}
public class PropertyTest
{
public PropertyTest()
{
NumRepet = 100000000;
_numRepet = NumRepet;
}
int NumRepet { get; set; }
private int _numRepet;
public int LoopUsingGetter()
{
int dummy = 314;
for (int i = 0; i < NumRepet; i++)
{
dummy++;
}
return dummy;
}
public int LoopUsingCopy()
{
int numRepetCopy = NumRepet;
int dummy = 314;
for (int i = 0; i < numRepetCopy; i++)
{
dummy++;
}
return dummy;
}
public int LoopUsingField()
{
int dummy = 314;
for (int i = 0; i < _numRepet; i++)
{
dummy++;
}
return dummy;
}
}
In Release mode on my machine I get:
Using copy: 0.029
Using getter: 0.054
Using field: 0.026
which in my case is a disaster - the most critical loop just can't use any properties if I want to get maximum performance.
What am I doing wrong here? I was thinking that these would be inlined by the JIT optimizer.
Getters/Setters are syntactic sugar for methods with a few special conventions ("value" variable in a setter", and no visible parameter list).
According to this article, "If any of the method's formal arguments are structs, the method will not be inlined." -- ints are structs. Therefore, I think this limitation applies.
I haven't looked at the IL produced by the following code, but I did get some interesting results that I think shows this working this way...
using System;
using System.Diagnostics;
public static class Program{
public static void Main()
{
PropertyTest propertyTest = new PropertyTest();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
propertyTest.LoopUsingField();
Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingBoxedGetter();
Console.WriteLine("Using boxed getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Restart();
propertyTest.LoopUsingUnboxedGetter();
Console.WriteLine("Using unboxed getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
}
}
public class PropertyTest
{
public PropertyTest()
{
_numRepeat = 1000000000L;
_field = 1;
Property = 1;
IntProperty = 1;
}
private long _numRepeat;
private object _field = null;
private object Property {get;set;}
private int IntProperty {get;set;}
public void LoopUsingBoxedGetter()
{
for (long i = 0; i < _numRepeat; i++)
{
var f = Property;
}
}
public void LoopUsingUnboxedGetter()
{
for (long i = 0; i < _numRepeat; i++)
{
var f = IntProperty;
}
}
public void LoopUsingField()
{
for (long i = 0; i < _numRepeat; i++)
{
var f = _field;
}
}
}
This produces.. ON MY MACHINE, OS X (recent version of Mono), these results (in seconds):
Using field: 2.606
Using boxed getter: 2.585
Using unboxed getter: 2.71
You say you are optimizing your code, but I am curious as to how, what the functionality is supposed to be, and what the source data coming into this is as well as it's size as this is clearly not "real" code. If you are parsing a large list of data in consider utilizing the BinarySearch functionality. This is significantly faster than, say the .Contains() function with very large sets of data.
List<int> myList = GetOrderedList();
if (myList.BinarySearch(someValue) < 0)
// List does not contain data
Perhaps you are simply looping through data. If you are looping through data and returning a value perhaps you may want to utilize the yield keyword. Additionally consider the potential use of the parallel library if you can, or utilize your own thread management.
This does not seem like what you want judging by the posted source but it was very generic so I figured this was worth mentioning.
public IEnumerable<int> LoopUsingGetter()
{
int dummy = 314;
for (int i = 0; i < NumRepet; i++)
{
dummy++;
yield return dummy;
}
}
[ThreadStatic]
private static int dummy = 314;
public static int Dummy
{
get
{
if (dummy != 314) // or whatever your condition
{
return dummy;
}
Parallel.ForEach (LoopUsingGetter(), (i)
{
//DoWork(), not ideal for given example, but due to the generic context this may help
dummy += i;
});
}
return dummy;
}
Follow the 80/20 performance rule instead of micro-optimizing.
Write code for maintainability, instead of performance.
Perhaps Assembly language is the fastest but that does not mean we should use Assembly language for all purposes.
You are running the loop 100 million times and the difference is 0.02 millisecond or 20 microseconds. Calling a function will have some overhead but in most cases it does not matter. You can trust the compiler to inline or do advanced things.
Directly accessing the field will be problematic in 99% of the cases as you will not have control of where all your variables are referenced and fixing at too many places when you find something is wrong.
You should stop the stop watch when it completes the loop, your stopwatch is still running when you are writing to console this can add additional time that can skew your results.
[Test]
public void GetterVsField()
{
PropertyTest propertyTest = new PropertyTest();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
propertyTest.LoopUsingCopy();
stopwatch.Stop();
Console.WriteLine("Using copy: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Reset();
stopwatch.Start();
propertyTest.LoopUsingGetter();
stopwatch.Stop();
Console.WriteLine("Using getter: " + stopwatch.ElapsedMilliseconds / 1000.0);
stopwatch.Reset();
stopwatch.Start();
propertyTest.LoopUsingField();
stopwatch.Stop();
Console.WriteLine("Using field: " + stopwatch.ElapsedMilliseconds / 1000.0);
}
You have to check if optimize code checkbox is checked.
If it is not checked, access to the property is still method call
If it is checked the property is in-lined and the performance is the same as with direct field access because the JITed code will be the same
There is more restriction about inlinig in X64 JIT compiler. More information about JIT64 inlining optimization is there:
David Broman's CLR Profiling API Blog: Tail call JIT conditions.
please see point #3 The caller or callee return a value type.
If your property will return reference type, the property getter will be in-lined.
It means that the property int NumRepet { get; set; } is not inlined but object NumRepet { get; set; } will be inlined if you don't break another restriction.
The optimization of X64 JIT is poor and this is why new one will be introduced as John mention

what takes up the most of time in this small function? weird, and profiling breakdown given

blockProperty is dictionary<string,string[]>
bool BlockMatch(IList<string> container, string block, int cut)
{
string[] blockArray = blockProperty[block];
int length = blockArray.Length - cut;
if (length > container.Count)
{
return false;
}
for (int i = 0; i < length; i++)
{
if (blockArray[length - 1 - i] != container[container.Count - 1 - i])
{
return false;
}
}
return true;
}
Columns are: inclusive Elapsed time, exclusive Elapsed time, inclusive percentage (of the whole program), exclusive percentage, number of calls.
How can i optimize the method according to the profiling breakdown? As I find it strange that the exclusive elapsed time of the method (6042) is more than a half of inclusive one (10095).
To break this down any further, you may (for testing purposes) split up the function to small "one-line-subfunctions" so you can see the profiling broken down even more. Another help will be a double click on the profiling line to show you the individual function calls with their relative time.
try
if(!blockArray[length - 1 - i].Equals( container[container.Count - 1 - i]) )) {return false;}
Its possible that traversing the array in reverse-order is doing this: from what I know, arrays are optimized for forward/sequential access. Furthermore you might be preventing the JIT from doing bounds-checking elimination with all that arithmetic. Try this:
for (int i = cut; i < blockArray.Length; i++)
{
if (!StringComparer.Ordinal.Equals(blockArray[i], container[i]))
return false;
}
However, in the end it depends on how many items you have in the array - if there are a lot there isn't much you can do (except use unsafe code, but that is only going to give you a tiny bit extra).
Edit: you can improve the speed of negative cases using HashCodes; at the cost of memory.
class StringAndHash
{
public int HashCode;
public string Value;
public StringAndHash(string value)
{
if (value == null)
HashCode = 0;
else
HashCode = StringComparer.Ordinal.GetHashCode(value.GetHashCode());
Value = value;
}
public static implicit operator string(StringAndHash value)
{
return value.Value;
}
public static implicit operator StringAndHash(string value)
{
return new StringAndHash(value);
}
public override int GetHashCode()
{
return HashCode;
}
public override bool Equals(object obj)
{
var sah = obj as StringAndHash;
if (!object.ReferenceEquals(sah, null))
{
return Equals(sah);
}
return base.Equals(obj);
}
public override bool Equals(StringAndHash obj)
{
return obj.HashCode == HashCode // This will improve perf in negative cases.
&& StringComparer.Ordinal.Equals(obj.Value, Value);
}
}
public Dictionary<string, StringAndHash[]> blockProperty;
bool BlockMatch(IList<StringAndHash> container, string block, int cut)
{
var blockArray = blockProperty[block];
var length = blockArray.Length - cut;
if (length > container.Count)
{
return false;
}
for (int i = cut; i < blockArray.Length; i++)
{
if (blockArray[i].Equals(container[i]))
{
return false;
}
}
return true;
}

Categories