Reputation: 2417
For some performance reason , I want to get array of List directly in C#
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,2765070d40f47b98
My code here:
private UIVertex[] GetArray(List<UIVertex> verts)
{
var aryF = typeof(List<UIVertex>).GetField("_items", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return aryF.GetValue(verts) as UIVertex[];
}
private void SetSize(List<UIVertex> list, int size)
{
sizeF = typeof(List<UIVertex>).GetField("_size",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
}
static List<UIVertex> verts
static UIVertex[] ary;
//...
ary = GetArray(verts);
In my case I can reuse 'verts' , But when 'verts' count change. my ary need to get again ( and it call reflection ) ,
reflection cost is too height , Can I just keep ary reference ? How to do it?
PS:
When I use List.Add
or List.Get
, It's trigger String.memcpy()
, It's too slow , It is why I want to use array directly.
And bacause I reuse List , so array Length not change uaually. I can just keep it reference to be fast.
PS 2.
here is modify before:
static List<T> Orgin = new List<T>(128);
void DO() // *it call a lot of times.*
{
GetListByAPI(Orgin);// here is API , So it must insert List<T>.
int Size = Orgin.Count() * 5; // size is unchanged in here.
if(Orgin.Capacity < Size)
{
Orgin.Capacity = Size;
}
for(int i = Orgin.Count() ; i < Size ;i++)
{
T t
Orgin.Add(t);// I need "Oring.Count() * 5 " Size Array for my logic.
//...
}
}
here is modify after :
static List<T> Orgin = new List<T>(128);
static T[] StaticAry;
void DO() // *it call a lot of times.*
{
bool LastOringCapacity = Orgin.Capacity;
GetListByAPI(Orgin);// here is API , So it must insert List<T>.
bool arrayRefChanged = Orgin.Capacity > LastOringCapacity ; // I Can know it. and get array again.
int Size = Orgin.Count() * 5; // size is unchanged in here.
SetSize(Oring, Size);//here set array legth of list by refection
StaticAry = GetArray(Orgin);//here I get array of list by reflection
for(int i = 0 ; i < size; i++)
{
T t = StaticAry[i];//just use it , avoid to use List.Add api.
//....
}
}
this static Orgin is reuse , So Oring.Capacity maybe not change. So array of Orgin is not resize too. And I want to get array adress. directly use it. But now. I need use reflection to get array in every single DO().
The complexity place is that
List<T>.Capacity
& Array.Length
& List<T>.Count()
are three differet value.
Upvotes: -1
Views: 318
Reputation: 116
for my future self or some one who might need this.
CollectionsMarshal.AsSpan
method will what you need to get access of the internal array this does not use any reflection to retrieve the value. this method is work with unsafe context, usually it return a span. so be careful how you use that. this can be found under System.Runtime.InteropServices
namespace. Since it's operates in an unsafe context, improper use can lead to undefined behavior. So be careful.
Upvotes: 1
Reputation:
When the List<T>Count
has changed (the Capacity
in fact), it is because the internal array of the list has been resized.
Resizing a larger array requires allocating a new one and copying its content and forget the old memory space.
In theory shrinking is not a problem and only requires "cutting" the array.
But due to the organization of memory's cells it is impossible to ensure a grow without using a totally new consecutive block with a full available space.
Array.Resize(T[], Int32) Method :
This method allocates a new array with the specified size, copies elements from the old array to the new one, and then replaces the old array with the new one. array must be a one-dimensional array.
Thus you can't avoid that: each time the array is resized, you need to get it again.
Therefore and unless you do such thing once time and being sure that the array's capacity does not change, I do not recommend the technique you use because it is difficult to ensure that you always have the right array and not an abandonned reference.
Thus you should use the already existing method List<T>.ToArray()
.
But in fact, and if you can, because you want speed, you should use an Array instead of a List
.
As you can see, here what the List
does when Capacity
or Count
over Capacity
changes in List<T>.Capacity :
T[] newItems = new T[value];
if (_size > 0) {
Array.Copy(_items, 0, newItems, 0, _size);
}
_items = newItems;
And here is the Add
method:
public void Add(T item) {
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size++] = item;
_version++;
}
Array.Resize()
does the same thing:
if (larray.Length != newSize) {
T[] newArray = new T[newSize];
Array.Copy(larray, 0, newArray, 0, larray.Length > newSize? newSize : larray.Length);
array = newArray;
}
This could have been optimized for the case of downsizing I think, but there may be some reason related to CLR or GC.
Upvotes: 1