Author Topic: TNet.List IEnumerable  (Read 14932 times)

ldw-bas

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 2
  • Posts: 32
    • View Profile
TNet.List IEnumerable
« on: February 27, 2014, 04:34:48 AM »
Hi!

I was working with the TNet.List and I figured it doesn't implement IEnumerable, while it does implement IEnumerator. IEnumerable is very useful for LINQ as you might know. From earlier post I have found that there is a disfavor against the .NET collections. Although I agree with on implementing a faster list. I do not agree on not using foreach and LINQ etc. Implementing IEnumerable on the TNet.List would not influence performance on any of the existing code. I usually prefer features before performance, I think it's better to do performance optimization afterwards. For my menu for example I don't care about performance at all. I don't think it's good to force this performance thought onto everyone by not implementing IEnumerable.


danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #1 on: February 27, 2014, 09:30:54 AM »
I was just about to post about this too. I ran into this while switching from the generic list to TNet.List.
TNet.List seems to replace a rather limited portion of the generic list. Another feature that is "missing" is Sort(). I have a class that implements IComparable which I would like to use with TNet.List.

While I can work around these things and possibly edit TNet.List to do what I want I really do not want to keep having it overwritten when updating.
Any chance of having these things added to TNet.List?

ldw-bas

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 2
  • Posts: 32
    • View Profile
Re: TNet.List IEnumerable
« Reply #2 on: February 27, 2014, 11:19:19 AM »
I have editted my version of the TNet List. I don't know if you used any kind of version control for yourself, in that case your can always retrieve your changes. Nevertheless consistency with the original library is always preferable. Since the more you change the harder it is for other to support you with issues. I try to change as little as possible to packages I use.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #3 on: February 27, 2014, 11:55:42 AM »
Yes, I am modifying mine as well just to be able to swap them if I want to. :)
I am adding:
- Count
- Implementing IEnumerable
- Implementing Sort using IComparable
...and we will see if something else pops up.

But, I agree with you. I would rather have this rolled into TNet proper both to ensure I am not introducing bugs and to make updating  of TNet effortless.
Aren/Michael, would you consider including these additions in the official version?

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNet.List IEnumerable
« Reply #4 on: February 27, 2014, 07:13:34 PM »
If you submit your modifications, I will have a look. You just reminded me that TNList's Sort function needs to be updated to what NGUI uses:
  1.         /// <summary>
  2.         /// List.Sort equivalent.
  3.         /// </summary>
  4.  
  5.         [DebuggerHidden]
  6.         [DebuggerStepThrough]
  7.         public void Sort (CompareFunc comparer)
  8.         {
  9.                 int start = 0;
  10.                 int max = size - 1;
  11.                 bool changed = true;
  12.  
  13.                 while (changed)
  14.                 {
  15.                         changed = false;
  16.  
  17.                         for (int i = start; i < max; ++i)
  18.                         {
  19.                                 // Compare the two values
  20.                                 if (comparer(buffer[i], buffer[i + 1]) > 0)
  21.                                 {
  22.                                         // Swap the values
  23.                                         T temp = buffer[i];
  24.                                         buffer[i] = buffer[i + 1];
  25.                                         buffer[i + 1] = temp;
  26.                                         changed = true;
  27.                                 }
  28.                                 else if (!changed)
  29.                                 {
  30.                                         // Nothing has changed -- we can start here next time
  31.                                         start = (i == 0) ? 0 : i - 1;
  32.                                 }
  33.                         }
  34.                 }
  35.         }
  36.  
  37.         /// <summary>
  38.         /// Comparison function should return -1 if left is less than right, 1 if left is greater than right, and 0 if they match.
  39.         /// </summary>
  40.  
  41.         public delegate int CompareFunc (T left, T right);
Keep in mind, using "foreach" leaks memory on iOS (bug in Unity). That's why I don't like using it.

Using iterator-based iteration is also quite a bit slower than just a simple for (int i = 0; i < list.size; ++i) loop. Convenience usually has a price.

Even the simple List.Count invokes a property, which has overhead. TNet's List.size is a variable, so there is no overhead.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #5 on: February 28, 2014, 02:40:18 AM »
I had no idea foreach had memory issues on iOS. How severe is it? Any idea why it has not been fixed? We have titles on the App Store which uses quite a bit of foreach (mainly to iterate through Hashtables) and while they perform well (few crash reports) no one likes memory leaks.

Regarding the other performance optimizations, the price for convenience (in this case generics compatibility) must be relatively small? Have you measured the effects and concluded they are significant enough to warrant the dropping of the features?

ldw-bas

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 2
  • Posts: 32
    • View Profile
Re: TNet.List IEnumerable
« Reply #6 on: February 28, 2014, 03:23:42 AM »
I am also curious to the memory leaks. A quick google action didn't show my any information. Since when is this bug known?

From my experience, usually you loose a lot of performance on a small number of features and not on calling properties instead of variables. Nevertheless, in the case of a package manufacturer I appreciate you take the effort to make it perform as optimal as possible since others kind of have to live with the decisions you make.

I can supply you with the IEnumerable implementation. It's a very small adjustment.

Change the header to:
  1. public class List<T> : System.Collections.Generic.IEnumerable<T>
  2.  

And add the following method:
  1. System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
  2. {
  3.    return GetEnumerator();
  4. }
  5.  

It is important to specify the IEnumerable before getEnumerator to tell the compiler you mean the non generic version of the method.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #7 on: February 28, 2014, 01:11:54 PM »
I spent a while this morning reading up on memory related issues in Unity3d. There is a fair bit of info available, even a Gamasutra article series on the subject. (Google Gamasutra Unity memory).

Anyway, I am probably revealing my age here but strictly speaking I would not call the issues "memory leaks". In older languages (C/C++ etc) memory leak was a situation where you failed to deallocate something and that memory was then "leaked" and you could never retrieve it. If you did it enough your application would crash. Period.

The issue at hand here is that that certain practices will allocate things on the heap where they need to be garbage collected. So they will be taken care of and you will get that memory back. However, garbage collection - especially the type apparently used by the mono version Unity uses - can be slow. So if you use "leaking" practices too much your game will likely stutter when the garbage collection kicks in. Sure it can crash too if you manage to leak too much memory before the GC has a chance to fix this, but you would have to be really careless IMHO.

I knew about the garbage collection problem, but I did not know that foreach and Ienumerable could add stuff to GC. Fortunately, the only places where we iterate lots of stuff using either method is in bigger operations that happen seldom and are user initiated. And we do call GC as well as Resource.Unload-whatever-it-is-called after (and sometimes during) these operations.
When running the game proper where fps is important we iterate using regular for loops and seldom do any fancy stuff. I think we may start a few coroutines sometimes so that is something I have to look at (that apparently causes overhead too - more than expected).

My take on the whole issue is that unless you are careless this is not much to worry about. I would advise to write nice code using the tools available (even if that is a foreach, coroutine, Linq etc.) and get it to run. Then profile and focus on the areas that truly are a problem. If you are smart and use the tools wisely you can most likely get a nice, well running game without having to do many .net workarounds if any.

Please note, that this is in no way criticism towards Aren or his implementation of TNet.List etc. I have not looked too much under the hood, but in frequent use (as I would suspect is the case in TNet) foreach, heavy list manipulation etc. *would* make a significant difference. So the custom version makes perfect sense. However, it may not make sense to rewrite your code to use it unless you will be relying on lists heavily many times / second.

Oops. Sorry for the length. Hope it may help someone. :)

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNet.List IEnumerable
« Reply #8 on: February 28, 2014, 09:08:36 PM »
@ldw-bas: TNet's List already has GetEnumerator() implemented, so it should be already usable in a foreach. I'm not sure what your changes would add.

@danfoo: GC is the devil in Unity. I've seen many Unity games that stutter every few frames when GC collection occurs. It's incredibly annoying and noticeable. Kerbal Space Program is a good example of that as it requires precision and finesse to begin with, and GC makes it incredibly frustrating at times.

Also... just google "unity foreach ios". http://makegamessa.com/discussion/1493/its-official-foreach-is-bad-in-unity/p1

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #9 on: March 01, 2014, 04:33:30 AM »
@ldw-bas: TNet's List already has GetEnumerator() implemented, so it should be already usable in a foreach. I'm not sure what your changes would add.
The case I stumbled upon was the inability to initialize the list when creating it ( new List() {bla, bla} ). Not sure idw's change solves that but I did track down how to do it the other day and it seemed very simple.
Quote
@danfoo: GC is the devil in Unity. I've seen many Unity games that stutter every few frames when GC collection occurs. It's incredibly annoying and noticeable. Kerbal Space Program is a good example of that as it requires precision and finesse to begin with, and GC makes it incredibly frustrating at times.

Also... just google "unity foreach ios". http://makegamessa.com/discussion/1493/its-official-foreach-is-bad-in-unity/p1
Yes, I know. I have read a bit about the ins and outs of the different Mono versions and speculation on why Unity has not moved to the later versions with generational GC (licensing issues?). My current stance is actually that it was easier to program in C++. Sure, you had to manually allocate and deallocate, but you knew that and you were careful to avoid any leaks. With C# you get a false sense of security and a lot of wishful black-boxing. It is actually harder to keep track of when things are left to GC. Thank god for the profiler... :)

As for Kerbal, I have not actually tried it. I am a bit surprised though that they have not been able to work around the GC issues if they are that obvious. Have to search and see if they have written anything about it.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #10 on: March 01, 2014, 04:53:35 AM »
I may as well mention how we seemingly have managed to avoid most GC issues. In code running frequently I tend to reuse most variables. I avoid allocating in often used functions and almost never do it in loops. Maybe it is not good and clean coding practice but given the problems with GC it is a good thing to do IMHO.

ldw-bas

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 2
  • Posts: 32
    • View Profile
Re: TNet.List IEnumerable
« Reply #11 on: March 01, 2014, 09:23:29 AM »
Ok, a lot has happened here since yesterday.

Quote
@ldw-bas: TNet's List already has GetEnumerator() implemented, so it should be already usable in a foreach. I'm not sure what your changes would add.
The IEnumerable implementation allows for the usage of LINQ. LINQ it totally build on IEnumerable and IQueryable. I am not sure if it also solves the "new List() {bla, bla}" issue danfoo showed.

I did google on memory unity ios foreach, but that didn't give any results, but your link indeed explains it and references the same gamasutra article danfoo mentioned, which I also had found during my memory search. But if I understand it correctly the gamasutra article actually says you should just use foreach. The memory leak issue only occurs when using as class instead of a struct for the Enumerator (which is not done by the .NET classes) and if you use the mono compiler. If I understood it correctly it says if come across issues, switch to the free .NET sdk compiler and you are fine.

I do agree with danfoo on the long post, memory leaks do no longer refer to the same phenomenon occuring in c++. Although I don't consider myself old I thought the same about it. For example some memoryleak that is mentioned is when eventlisteners are not unsubscribed. That is just something that you used be aware of but not really a leak or an error in C# since this is expected behaviour in a lot of cases.

I am a still a bit cynical about the GC issues, but I have not created high performance games in unity. Still I think those issues are solved reasonably quick, with profiling, but than again maybe I am underestimating the problem here.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #12 on: March 01, 2014, 10:27:20 AM »
I am a still a bit cynical about the GC issues, but I have not created high performance games in unity. Still I think those issues are solved reasonably quick, with profiling, but than again maybe I am underestimating the problem here.
It is not quite black or white. :)

Garbage collection in Unity is certainly enough of a problem that it can really, seriously ruin things for you if you are not aware of it and working to avoid it. You may find yourself in a situation where the game does hickup once every 1-2 seconds with a fps drop long enough to ruin gameplay. On modern desktops it is not that likely to happen unless you absolutely push things to the limit, but on mobile devices you will get there very quickly. Just trust me on that. Been there, done that. :)

On the other hand, it is not the bane of Unity that many do advocate. (There is a camp of people who generally frown upon C# for game development, and an all-purpose engine such as Unity in particular, but that is another debate). One just has to avoid allocating things in places where it quickly adds up, and if possible trigger GC manually when it will not be noticed. As I mentioned earlier, I have managed to code around it by simply avoiding allocations within performance critical functions, rather using variables in object scope (and sometimes even public for access from the outside). Sure it is dirty, but it does increase performance and avoids the GC monster.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: TNet.List IEnumerable
« Reply #13 on: March 01, 2014, 09:14:24 PM »
That's exactly what happens in KSP. After spending some time creating you ship you will start noticing that the game hiccups every 2 seconds for a very noticeable amount (~0.2 sec, I've seen it go up to 0.5 sec at one point, that was brutal).

And when your game freezes for half a second every few seconds... well you've got a big problem.

And that's on an i4930k 6-core CPU with 2400 Mhz memory and a 780 GTX card, so you can imagine how bad the problem can be on a lower end machine.

danfoo

  • Jr. Member
  • **
  • Thank You
  • -Given: 0
  • -Receive: 2
  • Posts: 93
    • View Profile
Re: TNet.List IEnumerable
« Reply #14 on: March 04, 2014, 08:12:39 AM »
I strongly suspect their problems lies with a combination of relatively heavy Physx use (I assume they use PhysX to connect and simulate the created crafts?). I am not that impressed with PhysX for a number of reasons. It works well if you take great care, but there are *many* pitfalls and performance issues.