Unfortunately, our loop expression hoister is not the best of our optimizations, I woudn't expect it to be removed in currently shipped CLR versions. The best thing you can do is just like you would do in VC, look at the disassembly, best way to do that currently, is using cordbg and disassembling, don't forget to enable optimizations (m JitOptimizations=1, IIRC) And of course, before you make your code less readable due to performance optimizations, make sure it is a bottleneck (for Example, DrawLine is likely to be much more expensive than the test you want to hoist out of the loop) -- David Notario Software Design Engineer, CLR JIT Compiler http://devdiary.xplsv.com [quoted text, click to view] "news.microsoft.com" <discussion@discussion.microsoft.com> wrote in message news:%23j2QxaZoDHA.2536@tk2msftngp13.phx.gbl... > How will the compiler / JIToptimise the following? > > for (int i = 0 ; i <= (stop - start)/step ; i++) > { > if (a = Axis.X) > { > g.DrawLine(blah > } > else > { > } > } > > > Would it make the test outside the for and save checking for each iteration? > like below... > > if (a ==Axis.X) > { > for (int i ... ) > { > g.DrawLine(...) > } > } > else > { > for (int i ... ) > { > g.DrawLine(...) > } > } > > Thus saving a test for each iteration on a == Axis.X > > > Would it do this or should I do this myself? > > I would presume and hope the (stop - start)/step is optimised as a constant > value in the loop so I dont have to put this before... > >
How will the compiler / JIToptimise the following? for (int i = 0 ; i <= (stop - start)/step ; i++) { if (a = Axis.X) { g.DrawLine(blah } else { } } Would it make the test outside the for and save checking for each iteration? like below... if (a ==Axis.X) { for (int i ... ) { g.DrawLine(...) } } else { for (int i ... ) { g.DrawLine(...) } } Thus saving a test for each iteration on a == Axis.X Would it do this or should I do this myself? I would presume and hope the (stop - start)/step is optimised as a constant value in the loop so I dont have to put this before...
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > From the tests that I have performed, the jitter does not appear to perform > this particular optimization on the code presented. However even if it did, > I would recommend that you do not rely on the optimizations of the jitter to > remedy inefficient code, I believe that we should always write the most > optimal code we can and then let the compilers/jitters do what they can from > there. As with compilers not all jitters are created equal.
I disagree strongly. I believe we should always write the most *readable* code we can. If, after careful benchmarking, we find one area of code which is causing a bottleneck, then it's worth considering making the code less readable for the sake of performance - making absolutely sure that the optimisations have the effect we hope they will through more careful benchmarking, of course. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Hi, From the tests that I have performed, the jitter does not appear to perform this particular optimization on the code presented. However even if it did, I would recommend that you do not rely on the optimizations of the jitter to remedy inefficient code, I believe that we should always write the most optimal code we can and then let the compilers/jitters do what they can from there. As with compilers not all jitters are created equal. Hope this helps Chris Taylor [quoted text, click to view] "news.microsoft.com" <discussion@discussion.microsoft.com> wrote in message news:%23j2QxaZoDHA.2536@tk2msftngp13.phx.gbl... > How will the compiler / JIToptimise the following? > > for (int i = 0 ; i <= (stop - start)/step ; i++) > { > if (a = Axis.X) > { > g.DrawLine(blah > } > else > { > } > } > > > Would it make the test outside the for and save checking for each iteration? > like below... > > if (a ==Axis.X) > { > for (int i ... ) > { > g.DrawLine(...) > } > } > else > { > for (int i ... ) > { > g.DrawLine(...) > } > } > > Thus saving a test for each iteration on a == Axis.X > > > Would it do this or should I do this myself? > > I would presume and hope the (stop - start)/step is optimised as a constant > value in the loop so I dont have to put this before... > >
Well, the reason im asking is because I read on MSDN that in some cases its BAD to optimise the code for optimisations done in the JIT and it confuses it. [quoted text, click to view] "Chris Taylor" <chris_taylor_za@hotmail.com> wrote in message news:ua3y5#coDHA.3612@TK2MSFTNGP11.phx.gbl... > Hi, > > From the tests that I have performed, the jitter does not appear to perform > this particular optimization on the code presented. However even if it did, > I would recommend that you do not rely on the optimizations of the jitter to > remedy inefficient code, I believe that we should always write the most > optimal code we can and then let the compilers/jitters do what they can from > there. As with compilers not all jitters are created equal. > > Hope this helps > > Chris Taylor > > "news.microsoft.com" <discussion@discussion.microsoft.com> wrote in message > news:%23j2QxaZoDHA.2536@tk2msftngp13.phx.gbl... > > How will the compiler / JIToptimise the following? > > > > for (int i = 0 ; i <= (stop - start)/step ; i++) > > { > > if (a = Axis.X) > > { > > g.DrawLine(blah > > } > > else > > { > > } > > } > > > > > > Would it make the test outside the for and save checking for each > iteration? > > like below... > > > > if (a ==Axis.X) > > { > > for (int i ... ) > > { > > g.DrawLine(...) > > } > > } > > else > > { > > for (int i ... ) > > { > > g.DrawLine(...) > > } > > } > > > > Thus saving a test for each iteration on a == Axis.X > > > > > > Would it do this or should I do this myself? > > > > I would presume and hope the (stop - start)/step is optimised as a > constant > > value in the loop so I dont have to put this before... > > > > > >
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > I agree entirely with your sentiments regarding readable code. However I > believe that it is quite possible to have well written optimized code that > is perfectly readable, particularly with a language like C#. I am not > talking about the kind of optimizations required to perform realtime > rendering at 60 frames per second, just good programming practice. Badly > written code can be just as unreadable. > > If my comments seemed to imply drastic optimization techniques should be the > norm I appologize.
While it's almost always possible to write code which performs "reasonably well" and is still optimally readable, there *will* be times when optimisation and readability conflict. Furthermore, optimisation isn't always obvious. For instance, take a very simple loop: string x = "hello"; foreach (char c in x) { // Do something with c } That would *perhaps* be more optimally written as: string x = "hello"; for (int i=0; i < x.Length; i++) { char c = x[i]; // Do something with c } or we could hoist the length: string x = "hello"; int length = x.Length; for (int i=0; i < length; i++) { char c = x[i]; // Do something with c } or perhaps (if ordering isn't an issue) string x = "hello"; for (int i=x.Length-1; i >=0; i--) { char c = x[i]; // Do something with c } Without testing these, it's hard to know whether *any* performance increase would be achieved, and even after testing we'd only know it for one particular architecture and framework version. (In 1.0, for instance, the first would be significantly slower than the second. In 1.1 any distinction has been drastically reduced.) Unless I had some firm test evidence to suggest that it was going to cause a *real* performance change, I'd always go with the first version, because I view it as the most readable. I certainly don't have time to test all four versions for every bit of code I write. All of the versions are reasonably readable in themselves, but I regard optimal readability as the *first* thing that should be achieved (correctness follows from readability, IMO) - I would only even *consider* making the code less readable for the sake of performance later on. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
I always do the simplest solution first then add complexity as needed.. [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a101bd6b28e72ab9899dc@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > From the tests that I have performed, the jitter does not appear to perform > > this particular optimization on the code presented. However even if it did, > > I would recommend that you do not rely on the optimizations of the jitter to > > remedy inefficient code, I believe that we should always write the most > > optimal code we can and then let the compilers/jitters do what they can from > > there. As with compilers not all jitters are created equal. > > I disagree strongly. I believe we should always write the most > *readable* code we can. If, after careful benchmarking, we find one > area of code which is causing a bottleneck, then it's worth considering > making the code less readable for the sake of performance - making > absolutely sure that the optimisations have the effect we hope they > will through more careful benchmarking, of course. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
Ive seen products that when sent to perf teams, they get so morphed about that they are unreadable - maybe this is due to the QUICK PERF FIXES or patching done for performance gains rather than being "designed in", and the same for the data stores, they become just unbelievablly different. Makes you think about how they where designed in the first place is such a transformation can take place. [quoted text, click to view] "Chris Taylor" <chris_taylor_za@hotmail.com> wrote in message news:e$y8LKeoDHA.1672@TK2MSFTNGP09.phx.gbl... > Hi, > > I agree entirely with your sentiments regarding readable code. However I > believe that it is quite possible to have well written optimized code that > is perfectly readable, particularly with a language like C#. I am not > talking about the kind of optimizations required to perform realtime > rendering at 60 frames per second, just good programming practice. Badly > written code can be just as unreadable. > > If my comments seemed to imply drastic optimization techniques should be the > norm I appologize. > > Regards > > Chris Taylor > > "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message > news:MPG.1a101bd6b28e72ab9899dc@msnews.microsoft.com... > > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > > From the tests that I have performed, the jitter does not appear to > perform > > > this particular optimization on the code presented. However even if it > did, > > > I would recommend that you do not rely on the optimizations of the > jitter to > > > remedy inefficient code, I believe that we should always write the most > > > optimal code we can and then let the compilers/jitters do what they can > from > > > there. As with compilers not all jitters are created equal. > > > > I disagree strongly. I believe we should always write the most > > *readable* code we can. If, after careful benchmarking, we find one > > area of code which is causing a bottleneck, then it's worth considering > > making the code less readable for the sake of performance - making > > absolutely sure that the optimisations have the effect we hope they > > will through more careful benchmarking, of course. > > > > -- > > Jon Skeet - <skeet@pobox.com> > > http://www.pobox.com/~skeet > > If replying to the group, please do not mail me too > >
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > I agree with you on the point of readable code, my comment was targeted at > the "art" of writting code that just works and hoping the JITter/compiler > will make sure it works well. This seems to be an ever increasing trend and > programmers no longer consider good programming practice. Maybe I have just > had a bad experience?
But why does it matter if that code isn't anywhere *near* to being a bottleneck? Unless you actually measure how long things take, quite often Of course, it helps to know some *really* bad things in terms of optimisation - I'm not suggesting that concatenating strings in loops without using StringBuilder is a good idea, for instance :) [quoted text, click to view] > I 100% agree, I would also use the foreach construct, however when > constructing a manual loop (if there is no enumerator) will always hoist the > length invocation unless of course I was expecting the length to change > (shiver at the thought, I would typically consider this very bad practice), > when used in the condition statement.
Why though? Because it made sense in a different language? Do you have any *evidence* that hoisting the length has any significant benefit? (I agree about the length change business, btw - except in the situation where you're going through a collection and removing some elements, and there I would often iterate backwards so as to avoid having to fiddle with the index variable directly - but I'd include a brief comment to explain why I wasn't doing the for loop in the more normal way :) [quoted text, click to view] > I also always use ++i or --i rather > than the more traditional i++ or i--. I do not believe these optimization > make for less readable code.
I disagree - even though it's only a very small point, the intent of the code is clearer to my mind without the extra variable hoisted out of the loop - the intent is "go from 0 to just under the length of the string" which is most clearly expressed as for (int i=0; i < x.Length; i++) - it says *exactly* what it means, and I trust the JITter to make the code perform reasonably well. If it *doesn't* hoist it, I'll find that out if and when that loop turns out to be a bottleneck. The chances of that are very slim - and then I'll measure it. The ++i vs i++ business is more personal taste - I very, very rarely use either of them in a case where it matters which one is used. I therefore prefer the more commonly used one - if everyone uses similar conventions, it's easier to read other people's code. Not a big problem in either case - but that's the difference between "readable" code and "as readable as possible" code, IMO. (Take another example: suppose I'd used the variable "a" instead of "i". Logically, it shouldn't make any difference in readability at all, but the fact that "i" is so conventionally used in this context makes it just that bit more familiar.) [quoted text, click to view] > And the latter "optimization" stems from my 12 > year love affair with C++.
Ah - I make a point of trying not to bring any idiomatic baggage from one language to another. I very consciously decided to adopt the .NET naming conventions rather than sticking with Java ones when I started learning C#, for instance. As I've said quite often, some of the worst Java code I've seen is from C++ programmers who are trying to bend Java into a C++-like shape. [quoted text, click to view] > I beg you not to take this personally, I think that in fact we would > actually be agreeing on most of what is said here was it not maybe for my > bad choice of wording or lack of elaboration in my initial post :).
No offence taken, I assure you, and none intended either :) Life would be very boring if we all agreed on absolutely everything! -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Hi, I agree entirely with your sentiments regarding readable code. However I believe that it is quite possible to have well written optimized code that is perfectly readable, particularly with a language like C#. I am not talking about the kind of optimizations required to perform realtime rendering at 60 frames per second, just good programming practice. Badly written code can be just as unreadable. If my comments seemed to imply drastic optimization techniques should be the norm I appologize. Regards Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a101bd6b28e72ab9899dc@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > From the tests that I have performed, the jitter does not appear to perform > > this particular optimization on the code presented. However even if it did, > > I would recommend that you do not rely on the optimizations of the jitter to > > remedy inefficient code, I believe that we should always write the most > > optimal code we can and then let the compilers/jitters do what they can from > > there. As with compilers not all jitters are created equal. > > I disagree strongly. I believe we should always write the most > *readable* code we can. If, after careful benchmarking, we find one > area of code which is causing a bottleneck, then it's worth considering > making the code less readable for the sake of performance - making > absolutely sure that the optimisations have the effect we hope they > will through more careful benchmarking, of course. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > >> Why though? Because it made sense in a different language? Do you have > >> any *evidence* that hoisting the length has any significant benefit? > > Well, this is actually one I always confirm. And both the generated IL as > well as the i386 assembler performs a function call every iteration.
It shouldn't need to - it should be able to inline the call to String.Length anyway. The generated IL will make the property method call, but that's not necessarily what the JIT will do. (When you looked at the generated assembly code, was that running in release mode, or under a debugger? The JIT compiler doesn't perform as many optimisations under debug as release mode.) I'd be very surprised if String.Length weren't inlined - although I've been surprised before, of course :) [quoted text, click to view] > Granted! this might not have any significant impact in 99% of the cases, but > I (and I agree I might be wrong here) always try to promote patterns like > these so that we start to think about the code we write, after all these > years I have not come accross a compiler/JITter that optimizes this.
It would have to know that String.Length was constant for any given string, of course, which it may well not to - but the actual method call should be converted into inline code, which may not be quite as fast as a local variable lookup, but should be fast enough for almost everything. Again, I believe that the optimised pattern is less readable than the slightly less efficient one, and I'd rather have more readable code for the 99% of cases and only go to less readable code - even *slightly* less readable code - for that 1% (or probably far fewer) cases where it makes a truly *significant* difference. [quoted text, click to view] > >> The ++i vs i++ business is more personal taste > > My reasoning here, I am well aware that in the case of primitive types where > the return value is ignored there is actually no difference in the generated > code, however when ever these operators are overloaded the post-increment is > typically implemented using a temporary variable which the compiler/JITter > does not optimize away. As I always try to treat datatypes as having certain > traits, allowing one datatype to be interchanged for a datatype with similar > traits, this helps to ensure that even when a class with overloaded ++ > operator I will have the benefit of the more optimal pre-increment operator. > Honestly I have not confirmed this in .NET as it handles the symantecs of > post/pre internally, that is why I made explicit reference to C++.
I suspect I wouldn't use the overloaded operator on a class which provided one anyway (unless it naughtily didn't provide an equivalent non-operator way of doing things) - I dislike overloaded operators in general, apart from a very few cases (string concatenation and (date/time)/timespan handling being the primary cases). I'd far rather code the more conventional way for the 99.99% of the time I'm not using the very, very occasional non-built-in type where using the ++ operator makes sense. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Hi, I agree with you on the point of readable code, my comment was targeted at the "art" of writting code that just works and hoping the JITter/compiler will make sure it works well. This seems to be an ever increasing trend and programmers no longer consider good programming practice. Maybe I have just had a bad experience? I 100% agree, I would also use the foreach construct, however when constructing a manual loop (if there is no enumerator) will always hoist the length invocation unless of course I was expecting the length to change (shiver at the thought, I would typically consider this very bad practice), when used in the condition statement. I also always use ++i or --i rather than the more traditional i++ or i--. I do not believe these optimization make for less readable code. And the latter "optimization" stems from my 12 year love affair with C++. I beg you not to take this personally, I think that in fact we would actually be agreeing on most of what is said here was it not maybe for my bad choice of wording or lack of elaboration in my initial post :). Best Regards Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a1028fac9f188069899e0@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > I agree entirely with your sentiments regarding readable code. However I > > believe that it is quite possible to have well written optimized code that > > is perfectly readable, particularly with a language like C#. I am not > > talking about the kind of optimizations required to perform realtime > > rendering at 60 frames per second, just good programming practice. Badly > > written code can be just as unreadable. > > > > If my comments seemed to imply drastic optimization techniques should be the > > norm I appologize. > > While it's almost always possible to write code which performs > "reasonably well" and is still optimally readable, there *will* be > times when optimisation and readability conflict. Furthermore, > optimisation isn't always obvious. For instance, take a very simple > loop: > > string x = "hello"; > foreach (char c in x) > { > // Do something with c > } > > That would *perhaps* be more optimally written as: > > string x = "hello"; > for (int i=0; i < x.Length; i++) > { > char c = x[i]; > // Do something with c > } > > or we could hoist the length: > > string x = "hello"; > int length = x.Length; > for (int i=0; i < length; i++) > { > char c = x[i]; > // Do something with c > } > > or perhaps (if ordering isn't an issue) > > string x = "hello"; > for (int i=x.Length-1; i >=0; i--) > { > char c = x[i]; > // Do something with c > } > > Without testing these, it's hard to know whether *any* performance > increase would be achieved, and even after testing we'd only know it > for one particular architecture and framework version. (In 1.0, for > instance, the first would be significantly slower than the second. In > 1.1 any distinction has been drastically reduced.) > > Unless I had some firm test evidence to suggest that it was going to > cause a *real* performance change, I'd always go with the first > version, because I view it as the most readable. I certainly don't have > time to test all four versions for every bit of code I write. All of > the versions are reasonably readable in themselves, but I regard > optimal readability as the *first* thing that should be achieved > (correctness follows from readability, IMO) - I would only even > *consider* making the code less readable for the sake of performance > later on. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > Quick update, just checked the ++i vs i++. The IL differs very slightly with > either a DUP before the increment or a DUP after, my first thought here was > that there would be no difference, but once I looked at the JIT'd code the > post increment requires an extra instruction and slower addressing rather > than faster register access on two counts. However, I am not trying to say > this is significant! Just sharing my findings since I had not previously > investigated this issue in .NET.
I presume that's when it's used in the middle of an expression, rather than on its own? On its own I get the IL code of: ldloc.0 ldc.i4.1 add stloc.0 for both versions. (As I said before, I very rarely use it at all in the middle of something else - it's just not clear enough. When I do, the semantics I want determine which version I use, rather than speed :) -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
[quoted text, click to view] news.microsoft.com <discussion@discussion.microsoft.com> wrote: > what if that string.Length changes during a loop :D say i append to the > string?
You can't append to the string itself (unless you're running in mscorlib). The JIT should be able to note that the variable's value (the string reference) doesn't change in the body of the loop. However, this *does* require some reasonably in-depth knowledge about the string type, so I'm not *hugely* surprised that it doesn't hoist the length out. (Out of interest Chris: could you check whether or not it hoists the length if you're iterating through an *array* rather than a string? If you could post how you're doing the testing, that would be great too - then I can do it myself in future :) Inlining shouldn't matter even if the variable's value *does* change - it would still get the right value. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
[quoted text, click to view] news.microsoft.com <discussion@discussion.microsoft.com> wrote: > Stringbuilder then. > > or anything .Length. > or something..
The property may or may not be inlined, but if it's going to change then the condition can't be hoisted from the loop. That's a separate decision from inlining. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
what if that string.Length changes during a loop :D say i append to the string? [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a104bbe11d378879899e9@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > >> Why though? Because it made sense in a different language? Do you have > > >> any *evidence* that hoisting the length has any significant benefit? > > > > Well, this is actually one I always confirm. And both the generated IL as > > well as the i386 assembler performs a function call every iteration. > > It shouldn't need to - it should be able to inline the call to > String.Length anyway. The generated IL will make the property method > call, but that's not necessarily what the JIT will do. (When you looked > at the generated assembly code, was that running in release mode, or > under a debugger? The JIT compiler doesn't perform as many > optimisations under debug as release mode.) > > I'd be very surprised if String.Length weren't inlined - although I've > been surprised before, of course :) > > > Granted! this might not have any significant impact in 99% of the cases, but > > I (and I agree I might be wrong here) always try to promote patterns like > > these so that we start to think about the code we write, after all these > > years I have not come accross a compiler/JITter that optimizes this. > > It would have to know that String.Length was constant for any given > string, of course, which it may well not to - but the actual method > call should be converted into inline code, which may not be quite as > fast as a local variable lookup, but should be fast enough for almost > everything. > > Again, I believe that the optimised pattern is less readable than the > slightly less efficient one, and I'd rather have more readable code for > the 99% of cases and only go to less readable code - even *slightly* > less readable code - for that 1% (or probably far fewer) cases where it > makes a truly *significant* difference. > > > >> The ++i vs i++ business is more personal taste > > > > My reasoning here, I am well aware that in the case of primitive types where > > the return value is ignored there is actually no difference in the generated > > code, however when ever these operators are overloaded the post-increment is > > typically implemented using a temporary variable which the compiler/JITter > > does not optimize away. As I always try to treat datatypes as having certain > > traits, allowing one datatype to be interchanged for a datatype with similar > > traits, this helps to ensure that even when a class with overloaded ++ > > operator I will have the benefit of the more optimal pre-increment operator. > > Honestly I have not confirmed this in .NET as it handles the symantecs of > > post/pre internally, that is why I made explicit reference to C++. > > I suspect I wouldn't use the overloaded operator on a class which > provided one anyway (unless it naughtily didn't provide an equivalent > non-operator way of doing things) - I dislike overloaded operators in > general, apart from a very few cases (string concatenation and > (date/time)/timespan handling being the primary cases). > > I'd far rather code the more conventional way for the 99.99% of the > time I'm not using the very, very occasional non-built-in type where > using the ++ operator makes sense. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > First how I test this. > 1) I write the code in a function > 2) I Call the function so that it get JIT'd > 3) I then call System.Diagnostics.Debugger.Break(); This breaks in a release > build > 4) I Call the function again, this way I am sure the JIT did not do a debug > JIT since this is the cached from the previous JIT -> Might be over cautious > here > 5) Display the dissassembler to view the i386 code > > For arrays eg. byte[] the length is checked directly against the internal > member -> Very Good > > For ArrayList the Count is not inlined or hoisted it is invoked on each > iteration -> Not So Good (Well in my books :) )
Hmmm... I'm still not sure whether you'll really be seeing the fully- JITted-with-all-optimisations code there. I don't know the best way of doing it though - ngen would give some idea. For ArrayList.Count not to be inlined is very odd - it's a prime candidate for inlining. Can you see *any* calls actually being inlined? Here's some code to demonstrate why I believe inlining is occurring with the String.Length call: (See http://www.pobox.com/~skeet/csharp/benchmark.html for the framework in which to run this code.) using System; using System.Runtime.CompilerServices; public class Test { static int iterations=100000; static int counter; static string testString = new string ('x', 100000); static int testStringLength = testString.Length; public static void Init (string[] args) { if (args.Length >= 1) iterations = Int32.Parse(args[0]); if (args.Length >= 2) { testString = new string ('x', Int32.Parse(args[1])); testStringLength = testString.Length; } } public static void Reset() { counter=0; } public static void Check() { if (counter != iterations*testString.Length) throw new Exception ("Counter value incorrect."); } [Benchmark] public static void TestHoisted() { int localIterations = iterations; string localString = testString; int localCounter = 0; for (int i=0; i < localIterations; i++) { int length = localString.Length; for (int j=0; j < length; j++) { localCounter++; } } counter = localCounter; } [Benchmark] public static void TestUnhoisted() { int localIterations = iterations; string localString = testString; int localCounter = 0; for (int i=0; i < localIterations; i++) { for (int j=0; j < localString.Length; j++) { localCounter++; } } counter = localCounter; } [Benchmark] public static void TestMethodCallNoInlining() { int localIterations = iterations; string localString = testString; int localCounter = 0; for (int i=0; i < localIterations; i++) { for (int j=0; j < GetStringLengthNoInlining(); j++) { localCounter++; } } counter = localCounter; } [MethodImpl(MethodImplOptions.NoInlining)] static int GetStringLengthNoInlining () { return testStringLength; } [Benchmark] public static void TestInlinedMethodCall() { int localIterations = iterations; string localString = testString; int localCounter = 0; for (int i=0; i < localIterations; i++) { for (int j=0; j < GetStringLengthInlined(); j++) { localCounter++; } } counter = localCounter; } static int GetStringLengthInlined () { return testStringLength; } } The results are: Benchmarking type Test Run #1 TestHoisted 00:00:06.9375000 TestUnhoisted 00:00:10.3437500 TestMethodCallNoInlining 00:00:40.2812500 TestInlinedMethodCall 00:00:06.7968750 Run #2 TestHoisted 00:00:06.6875000 TestUnhoisted 00:00:10.3281250 TestMethodCallNoInlining 00:00:40.1250000 TestInlinedMethodCall 00:00:06.9218750 Interestingly, it looks like the JIT *is* able to spot that the inlined method value can be hoisted, but not the String.Length property. However, as soon as we make the method call not inlined, it's *much* slower than using the unhoisted String.Length - and the only reason I can think of for that is that String.Length is inlined itself. (You might want to run that test using your check for inlining or not, as a way of testing its validity.) -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Stringbuilder then. or anything .Length. or something.. [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a10531d4645bf139899ec@msnews.microsoft.com... > news.microsoft.com <discussion@discussion.microsoft.com> wrote: > > what if that string.Length changes during a loop :D say i append to the > > string? > > You can't append to the string itself (unless you're running in > mscorlib). The JIT should be able to note that the variable's value > (the string reference) doesn't change in the body of the loop. However, > this *does* require some reasonably in-depth knowledge about the string > type, so I'm not *hugely* surprised that it doesn't hoist the length > out. (Out of interest Chris: could you check whether or not it hoists > the length if you're iterating through an *array* rather than a string? > If you could post how you're doing the testing, that would be great too > - then I can do it myself in future :) > > Inlining shouldn't matter even if the variable's value *does* change - > it would still get the right value. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote:
<snip> [quoted text, click to view] > In a release build, both cases the Length property is invoked with a 'CALL' > in the JIT'd code for each iteration.
See my other post for evidence that your check for the JIT'd code might not be valid - it looks to me like you're not seeing inlining when I think it *must* be happening. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
[quoted text, click to view] >> Why though? Because it made sense in a different language? Do you have >> any *evidence* that hoisting the length has any significant benefit?
Well, this is actually one I always confirm. And both the generated IL as well as the i386 assembler performs a function call every iteration. Granted! this might not have any significant impact in 99% of the cases, but I (and I agree I might be wrong here) always try to promote patterns like these so that we start to think about the code we write, after all these years I have not come accross a compiler/JITter that optimizes this. [quoted text, click to view] >> The ++i vs i++ business is more personal taste
My reasoning here, I am well aware that in the case of primitive types where the return value is ignored there is actually no difference in the generated code, however when ever these operators are overloaded the post-increment is typically implemented using a temporary variable which the compiler/JITter does not optimize away. As I always try to treat datatypes as having certain traits, allowing one datatype to be interchanged for a datatype with similar traits, this helps to ensure that even when a class with overloaded ++ operator I will have the benefit of the more optimal pre-increment operator. Honestly I have not confirmed this in .NET as it handles the symantecs of post/pre internally, that is why I made explicit reference to C++. Regards Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a103bced3c51efa9899e5@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > I agree with you on the point of readable code, my comment was targeted at > > the "art" of writting code that just works and hoping the JITter/compiler > > will make sure it works well. This seems to be an ever increasing trend and > > programmers no longer consider good programming practice. Maybe I have just > > had a bad experience? > > But why does it matter if that code isn't anywhere *near* to being a > bottleneck? Unless you actually measure how long things take, quite > often > > Of course, it helps to know some *really* bad things in terms of > optimisation - I'm not suggesting that concatenating strings in loops > without using StringBuilder is a good idea, for instance :) > > > I 100% agree, I would also use the foreach construct, however when > > constructing a manual loop (if there is no enumerator) will always hoist the > > length invocation unless of course I was expecting the length to change > > (shiver at the thought, I would typically consider this very bad practice), > > when used in the condition statement. > > Why though? Because it made sense in a different language? Do you have > any *evidence* that hoisting the length has any significant benefit? > > (I agree about the length change business, btw - except in the > situation where you're going through a collection and removing some > elements, and there I would often iterate backwards so as to avoid > having to fiddle with the index variable directly - but I'd include a > brief comment to explain why I wasn't doing the for loop in the more > normal way :) > > > I also always use ++i or --i rather > > than the more traditional i++ or i--. I do not believe these optimization > > make for less readable code. > > I disagree - even though it's only a very small point, the intent of > the code is clearer to my mind without the extra variable hoisted out > of the loop - the intent is "go from 0 to just under the length of the > string" which is most clearly expressed as > for (int i=0; i < x.Length; i++) > - it says *exactly* what it means, and I trust the JITter to make the > code perform reasonably well. If it *doesn't* hoist it, I'll find that > out if and when that loop turns out to be a bottleneck. The chances of > that are very slim - and then I'll measure it. > > The ++i vs i++ business is more personal taste - I very, very rarely > use either of them in a case where it matters which one is used. I > therefore prefer the more commonly used one - if everyone uses similar > conventions, it's easier to read other people's code. Not a big problem > in either case - but that's the difference between "readable" code and > "as readable as possible" code, IMO. (Take another example: suppose I'd > used the variable "a" instead of "i". Logically, it shouldn't make any > difference in readability at all, but the fact that "i" is so > conventionally used in this context makes it just that bit more > familiar.) > > > And the latter "optimization" stems from my 12 > > year love affair with C++. > > Ah - I make a point of trying not to bring any idiomatic baggage from > one language to another. I very consciously decided to adopt the .NET > naming conventions rather than sticking with Java ones when I started > learning C#, for instance. As I've said quite often, some of the worst > Java code I've seen is from C++ programmers who are trying to bend Java > into a C++-like shape. > > > I beg you not to take this personally, I think that in fact we would > > actually be agreeing on most of what is said here was it not maybe for my > > bad choice of wording or lack of elaboration in my initial post :). > > No offence taken, I assure you, and none intended either :) Life would > be very boring if we all agreed on absolutely everything! > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
[quoted text, click to view] Jon Skeet [C# MVP] <skeet@pobox.com> wrote: > > In a release build, both cases the Length property is invoked with a 'CALL' > > in the JIT'd code for each iteration. > > See my other post for evidence that your check for the JIT'd code might > not be valid - it looks to me like you're not seeing inlining when I > think it *must* be happening.
Just to clarify what I mean here: it looks to me like you're not seeing inlining when I think it would be happening with a full release- optimising JIT. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Hi, Quick update, just checked the ++i vs i++. The IL differs very slightly with either a DUP before the increment or a DUP after, my first thought here was that there would be no difference, but once I looked at the JIT'd code the post increment requires an extra instruction and slower addressing rather than faster register access on two counts. However, I am not trying to say this is significant! Just sharing my findings since I had not previously investigated this issue in .NET. Regards Chris Taylor [quoted text, click to view] "Chris Taylor" <chris_taylor_za@hotmail.com> wrote in message news:uEK5erfoDHA.424@TK2MSFTNGP10.phx.gbl... > >> Why though? Because it made sense in a different language? Do you have > >> any *evidence* that hoisting the length has any significant benefit? > > Well, this is actually one I always confirm. And both the generated IL as > well as the i386 assembler performs a function call every iteration. > Granted! this might not have any significant impact in 99% of the cases, but > I (and I agree I might be wrong here) always try to promote patterns like > these so that we start to think about the code we write, after all these > years I have not come accross a compiler/JITter that optimizes this. > > >> The ++i vs i++ business is more personal taste > > My reasoning here, I am well aware that in the case of primitive types where > the return value is ignored there is actually no difference in the generated > code, however when ever these operators are overloaded the post-increment is > typically implemented using a temporary variable which the compiler/JITter > does not optimize away. As I always try to treat datatypes as having certain > traits, allowing one datatype to be interchanged for a datatype with similar > traits, this helps to ensure that even when a class with overloaded ++ > operator I will have the benefit of the more optimal pre-increment operator. > Honestly I have not confirmed this in .NET as it handles the symantecs of > post/pre internally, that is why I made explicit reference to C++. > > Regards > > Chris Taylor > > "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message > news:MPG.1a103bced3c51efa9899e5@msnews.microsoft.com... > > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > > I agree with you on the point of readable code, my comment was targeted > at > > > the "art" of writting code that just works and hoping the > JITter/compiler > > > will make sure it works well. This seems to be an ever increasing trend > and > > > programmers no longer consider good programming practice. Maybe I have > just > > > had a bad experience? > > > > But why does it matter if that code isn't anywhere *near* to being a > > bottleneck? Unless you actually measure how long things take, quite > > often > > > > Of course, it helps to know some *really* bad things in terms of > > optimisation - I'm not suggesting that concatenating strings in loops > > without using StringBuilder is a good idea, for instance :) > > > > > I 100% agree, I would also use the foreach construct, however when > > > constructing a manual loop (if there is no enumerator) will always hoist > the > > > length invocation unless of course I was expecting the length to change > > > (shiver at the thought, I would typically consider this very bad > practice), > > > when used in the condition statement. > > > > Why though? Because it made sense in a different language? Do you have > > any *evidence* that hoisting the length has any significant benefit? > > > > (I agree about the length change business, btw - except in the > > situation where you're going through a collection and removing some > > elements, and there I would often iterate backwards so as to avoid > > having to fiddle with the index variable directly - but I'd include a > > brief comment to explain why I wasn't doing the for loop in the more > > normal way :) > > > > > I also always use ++i or --i rather > > > than the more traditional i++ or i--. I do not believe these > optimization > > > make for less readable code. > > > > I disagree - even though it's only a very small point, the intent of > > the code is clearer to my mind without the extra variable hoisted out > > of the loop - the intent is "go from 0 to just under the length of the > > string" which is most clearly expressed as > > for (int i=0; i < x.Length; i++) > > - it says *exactly* what it means, and I trust the JITter to make the > > code perform reasonably well. If it *doesn't* hoist it, I'll find that > > out if and when that loop turns out to be a bottleneck. The chances of > > that are very slim - and then I'll measure it. > > > > The ++i vs i++ business is more personal taste - I very, very rarely > > use either of them in a case where it matters which one is used. I > > therefore prefer the more commonly used one - if everyone uses similar > > conventions, it's easier to read other people's code. Not a big problem > > in either case - but that's the difference between "readable" code and > > "as readable as possible" code, IMO. (Take another example: suppose I'd > > used the variable "a" instead of "i". Logically, it shouldn't make any > > difference in readability at all, but the fact that "i" is so > > conventionally used in this context makes it just that bit more > > familiar.) > > > > > And the latter "optimization" stems from my 12 > > > year love affair with C++. > > > > Ah - I make a point of trying not to bring any idiomatic baggage from > > one language to another. I very consciously decided to adopt the .NET > > naming conventions rather than sticking with Java ones when I started > > learning C#, for instance. As I've said quite often, some of the worst > > Java code I've seen is from C++ programmers who are trying to bend Java > > into a C++-like shape. > > > > > I beg you not to take this personally, I think that in fact we would > > > actually be agreeing on most of what is said here was it not maybe for > my > > > bad choice of wording or lack of elaboration in my initial post :). > > > > No offence taken, I assure you, and none intended either :) Life would > > be very boring if we all agreed on absolutely everything! > > > > -- > > Jon Skeet - <skeet@pobox.com> > > http://www.pobox.com/~skeet > > If replying to the group, please do not mail me too > >
[quoted text, click to view] Chris Taylor <chris_taylor_za@hotmail.com> wrote: > It might well be, I have to think how I might view a ngen'd version, maybe I > will try with cordbg. If you have any ideas let me know.
I wish :) [quoted text, click to view] > The arrays were inlined.
I suspect that's because there's no code to be called "outline" as it were - it's not like there's a method call going on in the IL, it's just a ldlen instruction. (I also suspect that in a "real" situation that ldlen is hoisted - I'll have another go with my benchmarking framework later to see if the evidence supports that.) -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Just to confirm, all the test were done using a release build. .NET Framwork 1.1. [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a104bbe11d378879899e9@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > >> Why though? Because it made sense in a different language? Do you have > > >> any *evidence* that hoisting the length has any significant benefit? > > > > Well, this is actually one I always confirm. And both the generated IL as > > well as the i386 assembler performs a function call every iteration. > > It shouldn't need to - it should be able to inline the call to > String.Length anyway. The generated IL will make the property method > call, but that's not necessarily what the JIT will do. (When you looked > at the generated assembly code, was that running in release mode, or > under a debugger? The JIT compiler doesn't perform as many > optimisations under debug as release mode.) > > I'd be very surprised if String.Length weren't inlined - although I've > been surprised before, of course :) > > > Granted! this might not have any significant impact in 99% of the cases, but > > I (and I agree I might be wrong here) always try to promote patterns like > > these so that we start to think about the code we write, after all these > > years I have not come accross a compiler/JITter that optimizes this. > > It would have to know that String.Length was constant for any given > string, of course, which it may well not to - but the actual method > call should be converted into inline code, which may not be quite as > fast as a local variable lookup, but should be fast enough for almost > everything. > > Again, I believe that the optimised pattern is less readable than the > slightly less efficient one, and I'd rather have more readable code for > the 99% of cases and only go to less readable code - even *slightly* > less readable code - for that 1% (or probably far fewer) cases where it > makes a truly *significant* difference. > > > >> The ++i vs i++ business is more personal taste > > > > My reasoning here, I am well aware that in the case of primitive types where > > the return value is ignored there is actually no difference in the generated > > code, however when ever these operators are overloaded the post-increment is > > typically implemented using a temporary variable which the compiler/JITter > > does not optimize away. As I always try to treat datatypes as having certain > > traits, allowing one datatype to be interchanged for a datatype with similar > > traits, this helps to ensure that even when a class with overloaded ++ > > operator I will have the benefit of the more optimal pre-increment operator. > > Honestly I have not confirmed this in .NET as it handles the symantecs of > > post/pre internally, that is why I made explicit reference to C++. > > I suspect I wouldn't use the overloaded operator on a class which > provided one anyway (unless it naughtily didn't provide an equivalent > non-operator way of doing things) - I dislike overloaded operators in > general, apart from a very few cases (string concatenation and > (date/time)/timespan handling being the primary cases). > > I'd far rather code the more conventional way for the 99.99% of the > time I'm not using the very, very occasional non-built-in type where > using the ++ operator makes sense. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
Yes, that was used in a WriteLine statement. And I appreciate your feelings on this point with regards to readability, I am guilty of this :( those old C/C++ habbits die hard. Thank you for an enlightening discussion. Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a104ec8d7161039899ea@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > Quick update, just checked the ++i vs i++. The IL differs very slightly with > > either a DUP before the increment or a DUP after, my first thought here was > > that there would be no difference, but once I looked at the JIT'd code the > > post increment requires an extra instruction and slower addressing rather > > than faster register access on two counts. However, I am not trying to say > > this is significant! Just sharing my findings since I had not previously > > investigated this issue in .NET. > > I presume that's when it's used in the middle of an expression, rather > than on its own? On its own I get the IL code of: > > ldloc.0 > ldc.i4.1 > add > stloc.0 > > for both versions. > > (As I said before, I very rarely use it at all in the middle of > something else - it's just not clear enough. When I do, the semantics I > want determine which version I use, rather than speed :) > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
Hi, First how I test this. 1) I write the code in a function 2) I Call the function so that it get JIT'd 3) I then call System.Diagnostics.Debugger.Break(); This breaks in a release build 4) I Call the function again, this way I am sure the JIT did not do a debug JIT since this is the cached from the previous JIT -> Might be over cautious here 5) Display the dissassembler to view the i386 code For arrays eg. byte[] the length is checked directly against the internal member -> Very Good For ArrayList the Count is not inlined or hoisted it is invoked on each iteration -> Not So Good (Well in my books :) ) Regards Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a10531d4645bf139899ec@msnews.microsoft.com... > news.microsoft.com <discussion@discussion.microsoft.com> wrote: > > what if that string.Length changes during a loop :D say i append to the > > string? > > You can't append to the string itself (unless you're running in > mscorlib). The JIT should be able to note that the variable's value > (the string reference) doesn't change in the body of the loop. However, > this *does* require some reasonably in-depth knowledge about the string > type, so I'm not *hugely* surprised that it doesn't hoist the length > out. (Out of interest Chris: could you check whether or not it hoists > the length if you're iterating through an *array* rather than a string? > If you could post how you're doing the testing, that would be great too > - then I can do it myself in future :) > > Inlining shouldn't matter even if the variable's value *does* change - > it would still get the right value. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
Hi, Whenever the object being tested changes, since System.String is immutable appending to it actually creates a new string object with the new contents and the Length function is invoked on the new string object. In the case of StringBuilder the call to Length is also invoked each iteration, regardless of whether the length is changing or not. Hope this helps Chris Taylor [quoted text, click to view] "news.microsoft.com" <discussion@discussion.microsoft.com> wrote in message news:%23f7BLJgoDHA.3320@tk2msftngp13.phx.gbl... > Stringbuilder then. > > or anything .Length. > or something.. > > > "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message > news:MPG.1a10531d4645bf139899ec@msnews.microsoft.com... > > news.microsoft.com <discussion@discussion.microsoft.com> wrote: > > > what if that string.Length changes during a loop :D say i append to the > > > string? > > > > You can't append to the string itself (unless you're running in > > mscorlib). The JIT should be able to note that the variable's value > > (the string reference) doesn't change in the body of the loop. However, > > this *does* require some reasonably in-depth knowledge about the string > > type, so I'm not *hugely* surprised that it doesn't hoist the length > > out. (Out of interest Chris: could you check whether or not it hoists > > the length if you're iterating through an *array* rather than a string? > > If you could post how you're doing the testing, that would be great too > > - then I can do it myself in future :) > > > > Inlining shouldn't matter even if the variable's value *does* change - > > it would still get the right value. > > > > -- > > Jon Skeet - <skeet@pobox.com> > > http://www.pobox.com/~skeet > > If replying to the group, please do not mail me too > >
Hi, for ( int i = 0; i < sb.Length; ++i ) { // Code Not affecting the sb in any way } or for ( ; sb.Length < 10; ) { sb.Append( "a" ); } In a release build, both cases the Length property is invoked with a 'CALL' in the JIT'd code for each iteration. Regards Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a105cdea9d259609899ef@msnews.microsoft.com... > news.microsoft.com <discussion@discussion.microsoft.com> wrote: > > Stringbuilder then. > > > > or anything .Length. > > or something.. > > The property may or may not be inlined, but if it's going to change > then the condition can't be hoisted from the loop. That's a separate > decision from inlining. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
It might well be, I have to think how I might view a ngen'd version, maybe I will try with cordbg. If you have any ideas let me know. The arrays were inlined. Cheers Chris Taylor [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a1062a3813c52b09899f0@msnews.microsoft.com... > Chris Taylor <chris_taylor_za@hotmail.com> wrote: > > First how I test this. > > 1) I write the code in a function > > 2) I Call the function so that it get JIT'd > > 3) I then call System.Diagnostics.Debugger.Break(); This breaks in a release > > build > > 4) I Call the function again, this way I am sure the JIT did not do a debug > > JIT since this is the cached from the previous JIT -> Might be over cautious > > here > > 5) Display the dissassembler to view the i386 code > > > > For arrays eg. byte[] the length is checked directly against the internal > > member -> Very Good > > > > For ArrayList the Count is not inlined or hoisted it is invoked on each > > iteration -> Not So Good (Well in my books :) ) > > Hmmm... I'm still not sure whether you'll really be seeing the fully- > JITted-with-all-optimisations code there. I don't know the best way of > doing it though - ngen would give some idea. > > For ArrayList.Count not to be inlined is very odd - it's a prime > candidate for inlining. Can you see *any* calls actually being inlined? > > Here's some code to demonstrate why I believe inlining is occurring > with the String.Length call: > > (See http://www.pobox.com/~skeet/csharp/benchmark.html for the > framework in which to run this code.) > > using System; > using System.Runtime.CompilerServices; > > public class Test > { > static int iterations=100000; > static int counter; > static string testString = new string ('x', 100000); > static int testStringLength = testString.Length; > > public static void Init (string[] args) > { > if (args.Length >= 1) > iterations = Int32.Parse(args[0]); > if (args.Length >= 2) > { > testString = new string ('x', Int32.Parse(args[1])); > testStringLength = testString.Length; > } > } > > public static void Reset() > { > counter=0; > } > > public static void Check() > { > if (counter != iterations*testString.Length) > throw new Exception ("Counter value incorrect."); > } > > [Benchmark] > public static void TestHoisted() > { > int localIterations = iterations; > string localString = testString; > int localCounter = 0; > > for (int i=0; i < localIterations; i++) > { > int length = localString.Length; > for (int j=0; j < length; j++) > { > localCounter++; > } > } > > counter = localCounter; > } > > [Benchmark] > public static void TestUnhoisted() > { > int localIterations = iterations; > string localString = testString; > int localCounter = 0; > > for (int i=0; i < localIterations; i++) > { > for (int j=0; j < localString.Length; j++) > { > localCounter++; > } > } > > counter = localCounter; > } > > [Benchmark] > public static void TestMethodCallNoInlining() > { > int localIterations = iterations; > string localString = testString; > int localCounter = 0; > > for (int i=0; i < localIterations; i++) > { > for (int j=0; j < GetStringLengthNoInlining(); j++) > { > localCounter++; > } > } > > counter = localCounter; > } > > [MethodImpl(MethodImplOptions.NoInlining)] > static int GetStringLengthNoInlining () > { > return testStringLength; > } > > [Benchmark] > public static void TestInlinedMethodCall() > { > int localIterations = iterations; > string localString = testString; > int localCounter = 0; > > for (int i=0; i < localIterations; i++) > { > for (int j=0; j < GetStringLengthInlined(); j++) > { > localCounter++; > } > } > > counter = localCounter; > } > > static int GetStringLengthInlined () > { > return testStringLength; > } > } > > > The results are: > Benchmarking type Test > Run #1 > TestHoisted 00:00:06.9375000 > TestUnhoisted 00:00:10.3437500 > TestMethodCallNoInlining 00:00:40.2812500 > TestInlinedMethodCall 00:00:06.7968750 > Run #2 > TestHoisted 00:00:06.6875000 > TestUnhoisted 00:00:10.3281250 > TestMethodCallNoInlining 00:00:40.1250000 > TestInlinedMethodCall 00:00:06.9218750 > > Interestingly, it looks like the JIT *is* able to spot that the inlined > method value can be hoisted, but not the String.Length property. > However, as soon as we make the method call not inlined, it's *much* > slower than using the unhoisted String.Length - and the only reason I > can think of for that is that String.Length is inlined itself. > > (You might want to run that test using your check for inlining or not, > as a way of testing its validity.) > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
AFAIK the JITter not only hoists the length call but also elimintaes the bounds checks. So for(int i = 0; i < array.Length; ++i) { } is actually quicker than int length = array.Length for(int i = 0; i < length; ++i) { } [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a10531d4645bf139899ec@msnews.microsoft.com... > news.microsoft.com <discussion@discussion.microsoft.com> wrote: > > what if that string.Length changes during a loop :D say i append to the > > string? > > You can't append to the string itself (unless you're running in > mscorlib). The JIT should be able to note that the variable's value > (the string reference) doesn't change in the body of the loop. However, > this *does* require some reasonably in-depth knowledge about the string > type, so I'm not *hugely* surprised that it doesn't hoist the length > out. (Out of interest Chris: could you check whether or not it hoists > the length if you're iterating through an *array* rather than a string? > If you could post how you're doing the testing, that would be great too > - then I can do it myself in future :) > > Inlining shouldn't matter even if the variable's value *does* change - > it would still get the right value. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
[quoted text, click to view] "Chris Taylor" <chris_taylor_za@hotmail.com> wrote in message news:uyH0HyeoDHA.372@TK2MSFTNGP11.phx.gbl... > I also always use ++i or --i rather > than the more traditional i++ or i--. I do not believe these optimization > make for less readable code. And the latter "optimization" stems from my 12 > year love affair with C++.
Well done, Chris! I am myself a "prefixally incrementer". I have never understood why K&R intially promoted the more semantically complex "i++" over "++i" in their examples for the "C" language.
i++ is easier to read than ++i, maybe thats because its so well used more than ++i When we get to runtime environemnts with optimisation, readability is more of a focus. [quoted text, click to view] "Jerome Bonnet" <please@dontsp.am> wrote in message news:egNrqBFpDHA.1632@TK2MSFTNGP10.phx.gbl... > > "Chris Taylor" <chris_taylor_za@hotmail.com> wrote in message > news:uyH0HyeoDHA.372@TK2MSFTNGP11.phx.gbl... > > I also always use ++i or --i rather > > than the more traditional i++ or i--. I do not believe these optimization > > make for less readable code. And the latter "optimization" stems from my > 12 > > year love affair with C++. > > Well done, Chris! I am myself a "prefixally incrementer". I have never > understood why K&R intially promoted the more semantically complex "i++" > over "++i" in their examples for the "C" language. > >
Sorry, the method body in both cases should have been something like: Foo(array[i]); Apologies. [quoted text, click to view] "Stu Smith" <stuarts@remove.digita.com> wrote in message news:OoovpA8oDHA.744@tk2msftngp13.phx.gbl... > AFAIK the JITter not only hoists the length call but also elimintaes the > bounds checks. > > So > > for(int i = 0; i < array.Length; ++i) > { > } > > is actually quicker than > > int length = array.Length > > for(int i = 0; i < length; ++i) > { > } > > "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message > news:MPG.1a10531d4645bf139899ec@msnews.microsoft.com... > > news.microsoft.com <discussion@discussion.microsoft.com> wrote: > > > what if that string.Length changes during a loop :D say i append to the > > > string? > > > > You can't append to the string itself (unless you're running in > > mscorlib). The JIT should be able to note that the variable's value > > (the string reference) doesn't change in the body of the loop. However, > > this *does* require some reasonably in-depth knowledge about the string > > type, so I'm not *hugely* surprised that it doesn't hoist the length > > out. (Out of interest Chris: could you check whether or not it hoists > > the length if you're iterating through an *array* rather than a string? > > If you could post how you're doing the testing, that would be great too > > - then I can do it myself in future :) > > > > Inlining shouldn't matter even if the variable's value *does* change - > > it would still get the right value. > > > > -- > > Jon Skeet - <skeet@pobox.com> > > http://www.pobox.com/~skeet > > If replying to the group, please do not mail me too > >
[quoted text, click to view] Jerome Bonnet <please@dontsp.am> wrote: > Come on. That is completely subjective. If Kernighan and Ritchie had written > their examples with "++i" instead of "i++" you would write just the > opposite.
It's not completely subjective, but it *is* very difficult to what would be more readable just on instinct. You'd need a decent study with people who had no bias beforehand. Personally I *think* I prefer i++ because it tells me what I'm operating on before the operation itself, but I'm happy to admit that may well just be bias due to using it more often. One thing *is* important though: due to K&R (or whatever) many (most, I suspect) *existing* devlopers find i++ more readable than ++i, and as it makes no difference in managed code when it's a statement on its own (assuming the compiler is being reasonable), I think it's a better idea to use the more familiar form. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
Come on. That is completely subjective. If Kernighan and Ritchie had written their examples with "++i" instead of "i++" you would write just the opposite. [quoted text, click to view] "news.microsoft.com" <discussion@discussion.microsoft.com> wrote in message news:O0lIqEFpDHA.3732@tk2msftngp13.phx.gbl... > i++ is easier to read than ++i, maybe thats because its so well used more > than ++i
[quoted text, click to view] <discussion@discussion.microsoft.com> wrote: > The compiler and runtime should work FOR ME not me work FOR IT.
Um, yes. And the compiler *does* optimise i++ and ++i to the same IL - at least, the C# compiler does. So the real question is which is the most readable, which is what my post was about. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
[quoted text, click to view] Jerome Bonnet <please@dontsp.am> wrote: > This is irrelevant (and it's also a clich=E9). > I don't prefer "++i" because it helps the compiler, I prefer "++i" becaus= e > it is the most accurate description of what I want to achieve: simply > increment variable i. On the other hand, "i++" means "take the value for = i, > then increment i", which has the same effect when the value of the > expression is not used, but is sementically more compex than needed for w= hat > I want to achieve.
No - the only difference is in terms of the value of the expression.=20 One means "the value is that of i; increment i". The other is=20 "increment i; the value is that of i". I don't see that either is more=20 complicated than the other. --=20 Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet
The compiler and runtime should work FOR ME not me work FOR IT. [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a15b62f829fed1a989a41@msnews.microsoft.com... > Jerome Bonnet <please@dontsp.am> wrote: > > Come on. That is completely subjective. If Kernighan and Ritchie had written > > their examples with "++i" instead of "i++" you would write just the > > opposite. > > It's not completely subjective, but it *is* very difficult to what > would be more readable just on instinct. You'd need a decent study with > people who had no bias beforehand. > > Personally I *think* I prefer i++ because it tells me what I'm > operating on before the operation itself, but I'm happy to admit that > may well just be bias due to using it more often. > > One thing *is* important though: due to K&R (or whatever) many (most, I > suspect) *existing* devlopers find i++ more readable than ++i, and as > it makes no difference in managed code when it's a statement on its own > (assuming the compiler is being reasonable), I think it's a better idea > to use the more familiar form. > > -- > Jon Skeet - <skeet@pobox.com> > http://www.pobox.com/~skeet > If replying to the group, please do not mail me too
This is irrelevant (and it's also a cliché). I don't prefer "++i" because it helps the compiler, I prefer "++i" because it is the most accurate description of what I want to achieve: simply increment variable i. On the other hand, "i++" means "take the value for i, then increment i", which has the same effect when the value of the expression is not used, but is sementically more compex than needed for what I want to achieve. For me writing "i++" instead of "++i" is not wrong, it is just unecessarily complex, just like writing "i = i + 1" or "i = i++" or "i = i + 1 | 0x00 * 1" or whatever. [quoted text, click to view] <discussion@discussion.microsoft.com> wrote in message news:OoZ2E2TpDHA.3024@tk2msftngp13.phx.gbl... > The compiler and runtime should work FOR ME not me work FOR IT.
Whatever gets you wet basically. I prefer to use butter but hey thats my preference. [quoted text, click to view] "Jerome Bonnet" <please@dontsp.am> wrote in message news:e$lO7AUpDHA.392@TK2MSFTNGP11.phx.gbl... > This is irrelevant (and it's also a cliché). > I don't prefer "++i" because it helps the compiler, I prefer "++i" because > it is the most accurate description of what I want to achieve: simply > increment variable i. On the other hand, "i++" means "take the value for i, > then increment i", which has the same effect when the value of the > expression is not used, but is sementically more compex than needed for what > I want to achieve. > For me writing "i++" instead of "++i" is not wrong, it is just unecessarily > complex, just like writing "i = i + 1" or "i = i++" or "i = i + 1 | 0x00 * > 1" or whatever. > > <discussion@discussion.microsoft.com> wrote in message > news:OoZ2E2TpDHA.3024@tk2msftngp13.phx.gbl... > > The compiler and runtime should work FOR ME not me work FOR IT. > >
I see your point. I suppose I must be C++-biased then, just like Chris Taylor. [quoted text, click to view] "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message news:MPG.1a15c7e778eef5ac989a46@msnews.microsoft.com... >Jerome Bonnet <please@dontsp.am> wrote: >> This is irrelevant (and it's also a cliché). >> I don't prefer "++i" because it helps the compiler, I prefer "++i" because >> it is the most accurate description of what I want to achieve: simply >> increment variable i. On the other hand, "i++" means "take the value for i, >> then increment i", which has the same effect when the value of the >> expression is not used, but is sementically more compex than needed for what >> I want to achieve. > No - the only difference is in terms of the value of the expression. > One means "the value is that of i; increment i". The other is > "increment i; the value is that of i". I don't see that either is more > complicated than the other.
FWIW, the optimization guidelines suggest you use this method for iterating through an array: [quoted text, click to view] > for ( int i = 0; i < sb.Length; ++i ) > { > // Code Not affecting the sb in any way > }
Esp. If you are indexing the array with your loop parameter. Why? Because the compiler detects your loop validates the array index and removes the index validation code (because it knows you've already validated it), which could mean a large performance gain. AFAIK hoisting the sb.Length out of the loop is not recommended because it goes against this optimization. n!
I've read, in this group and elsewhere, that loop invariants will be hoisted outside of the loop as part of the optimization. All the profiling data I've collected (by means of DevPartner's profiler and others) conflicts with this assertion. Let's take the following: public SomeClass { public static int DoSomethingWithAString(string text) { for (int I = 0; I < text.Length; ++i) { // blah blah blah } } } Since I'm working on pattern-matching algorithms, I'm very concerned about this problem. String.get_Length() should be called only as many times as the function itself. This does *not* seem to happen, i.e., get_Length() is in fact called on every iteration. You can imagine how badly this affects pattern-matching. Yes, I've compiled it to release mode with optimization turned on. It may be that profiling affects the optimization; if so, please tell me. A C++ compiler would have no trouble hoisting this expression outside of the loop if text were a "reference to const," but of course there's no such thing in C#. I would normally consider it bad style to assign text.Length to a local variable, i.e., manually optimize the code, but I can't afford the performance penalty here. Tony [quoted text, click to view] > > Why? Because the compiler detects your loop validates the array index and > removes the index validation code (because it knows you've already validated > it), which could mean a large performance gain. AFAIK hoisting the sb.Length > out of the loop is not recommended because it goes against this > optimization.
Or try using a hoistable item like string.Chars.Length -- Justin Rogers DigiTec Web Consultants, LLC. [quoted text, click to view] <discussion@discussion.microsoft.com> wrote in message news:eatVPGG3DHA.3656@TK2MSFTNGP11.phx.gbl... > Hoist it out yourself then if its not being optimised that way. > > > > "Jon Skeet [C# MVP]" <skeet@pobox.com> wrote in message > news:MPG.1a722fc626ae6464989e8c@msnews.microsoft.com... > > Tony Nassar <tnassar@theanalysiscorp.com> wrote: > > > I've read, in this group and elsewhere, that loop invariants will be > hoisted > > > outside of the loop as part of the optimization. All the profiling data > I've > > > collected (by means of DevPartner's profiler and others) conflicts with > this > > > assertion. Let's take the following: > > > > > > public SomeClass > > > { > > > public static int DoSomethingWithAString(string text) > > > { > > > for (int I = 0; I < text.Length; ++i) > > > { > > > // blah blah blah > > > } > > > } > > > } > > > > > > Since I'm working on pattern-matching algorithms, I'm very concerned > about > > > this problem. String.get_Length() should be called only as many times as > the > > > function itself. This does *not* seem to happen, i.e., get_Length() is > in > > > fact called on every iteration. > > > > That's because the JIT doesn't know that the Length property isn't an > > invariant. (In fact, it's not an invariant really - it's only invariant > > to outside assemblies. The length can change within mscorlib.) > > > > It does, however, know about arrays, so long as the array expression > > itself doesn't change in the loop. > > > > > You can imagine how badly this affects pattern-matching. > > > > Not really, as I would imagine that text.Length is inlined anyway, so > > it's just a different memory comparison. > > > > > Yes, I've compiled it to release mode with optimization > > > turned on. It may be that profiling affects the optimization; if so, > please > > > tell me. A C++ compiler would have no trouble hoisting this expression > > > outside of the loop if text were a "reference to const," but of course > > > there's no such thing in C#. I would normally consider it bad style to > > > assign text.Length to a local variable, i.e., manually optimize the > code, > > > but I can't afford the performance penalty here. > > > > And what kind of performance penalty *are* you getting? A test I've run > > suggests that the performance difference isn't that big - about the > > same as an increment of a local int variable for each iteration. > > Certinaly not the kind of thing I'd worry about for most code - I'd > > wait until this method |