|
Wow, amazing tip!
|
|
|
|
|
I prefer #1. It might even be faster since AddRange() might do clever stuff under the hhod
"If we don't change direction, we'll end up where we're going"
|
|
|
|
|
megaadam wrote: It might even be faster since AddRange() might do clever stuff under the hhod
Unfortunately not. Since the OfType method doesn't return an ICollection<T> , the list can't pre-allocate enough space for the new items, since it doesn't know how many there will be.
Reference Source[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
An ultra pedantic code reviewer would suggest that
foreach (var item in source)
{
if (item is A a)
list.Add(a);
if (item is B b)
DoThing(b);
} could be improved by doing
foreach (var item in source)
{
if (item is A a)
list.Add(a);
else if (item is B b)
DoThing(b);
} or
foreach (var item in source)
{
if (item is B b)
DoThing(b);
else if (item is A a)
list.Add(a);
} as that would save rechecking found items. This would, however, possibly not give the desired effect if an item could be both an A and B (e.g. if A inherits B, B inherits A, or A / B are interfaces and the item can implement both interfaces).
Of course, you should spent a couple of weeks analysing these two code snippets with multiple copies of sample data to see which one saves the most milliseconds per decade
So, as others have said, ignore the code reviewer and do what makes sense to you.
|
|
|
|
|
Seems like a niche use-case, but how about something like this?
static class Extensions
{
public static IEnumerable<B> SplitList<T, A, B>(
this IEnumerable<T> source,
IList<A> listOfA)
where A : T
where B : T
{
foreach (T item in source)
{
switch (item)
{
case A a:
{
listOfA.Add(a);
break;
}
case B b:
{
yield return b;
break;
}
}
}
}
}
List<A> list = new();
DoThings(source.SplitList<YourBaseType, A, B>(list));
static void DoThings(IEnumerable<B> items)
{
foreach (var b in items)
{
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I would measure them to be sure.
But I agree that calling DoThings once is likely to be better than calling DoThing a great many times.
The particular micro-optimization I'm working on now will likely lead to a post in Weird and Wonderful.
|
|
|
|
|
well.. I did think that too....
then I did a simple test home and, well, to my stunned surprise the optimisation was 20% faster!
|
|
|
|
|
I'm guessing the refactor is faster since it only has to enum once.
To err is human. Fortune favors the monsters.
|
|
|
|
|
but it call a method for every item.....
I was unsure, or maybe thought it was going to be a bit in fact slower (extra method call, where enumeration is cheap)...
Anyway, I was so annoyed I made a simple test program at home and.. turns out new method is faster indeed (by 20% on my simple test thing)
After that, well, I beautify the new code a bit and rolled with it!
|
|
|
|
|
Looking at the code I would expect the second to be faster. The reason is that enumeration is slow relative to function calls. The original code enumerates three times over source and creates temporary objects. The rewritten code enumerates once over source and creates no temporary objects.
Super Lloyd actually tested and found the second code is 20% faster. The Lounge
|
|
|
|
|
In my experience with codegen method calls don't require much overhead. I haven't run any hard tests directly, but I have written a codegen system that did method calls to separate the non-terminals and one that did not. The one that did was significantly faster, I suspect due to keeping repeatedly used code cached at the CPU level whereas with longer methods that's not possible.
That's what leads me to lean toward what I leaned toward. I suspect JIT magic to reduce overhead, but there's not much you can do to with JIT to avoid allocation overhead. With .NET allocation is cheap in theory, but people forget about the overhead of the constructors.
To err is human. Fortune favors the monsters.
|
|
|
|
|
nice hindsight!
|
|
|
|
|
You know what you might try, if the container is indexable (your code is way up top of the thread and I don't recall it offhand) is do the loops without the enumerator.
for(int i = 0; i < container.Count; ++i) { ...
Like that. My bet is your results will be more similar at that point, but if i had to guess which one would be faster, I still suspect the method call one will be faster due to the overhead of running loops.
To err is human. Fortune favors the monsters.
|
|
|
|
|
I'd argue that you really need two lists, one of Type[A] and one of Type[B] (sorry, couldn't figure out how to avoid font changes with the <>). This appears to really be an issue of delaying the decision until too deep in the code's decision trees. The new code is easier to read and it really points out that your list should be two lists.
|
|
|
|
|
To me, this is a clear case of premature optimization and (almost) every optimization is premature if it sacrifices clarity when the need for optimization has not be demonstrated. There are certain cases involving function calls that I know for a fact can be done better so I deal with those up front rather than later since the delta time spent is zero.
Here's an exmple : x*x is considerably faster than pow(x,2) so I deal with that up front instead of returning to it later. Especially since calling the pow function does not enhance clarity.
"They have a consciousness, they have a life, they have a soul! Damn you! Let the rabbits wear glasses! Save our brothers! Can I get an amen?"
|
|
|
|
|
I am not C# programmer, but I would prefer it was written in C. Overhead is easier to spot.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
Performance is not the main reason to avoid multiple enumeration. Depending on the origin of the source, the collection may be changed between any two enumerations, so the result will be a bug that is extremely hard to reproduce.
|
|
|
|
|
Did you ever try to eat a clock?
It's time consuming, especially if you go back for seconds.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
No, I couldn't face it.
[Sorry folks, I know I really shouldn't encourage him...]
|
|
|
|
|
Then I'll have to hand it to you if you can dial it back.
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
Ate a clock?
Isn’t that a little late for dinner?
If you can't laugh at yourself - ask me and I will do it for you.
|
|
|
|
|
Non-stop punishment!
"A little time, a little trouble, your better day"
Badfinger
|
|
|
|
|
Message Closed
modified 31-May-22 22:42pm.
|
|
|
|
|
This isn't a forum for programming questions. Please submit your question by mousing over "quick answers" under the site's banner and clicking on "Ask a Question".
|
|
|
|
|