[corefoundation] Use `CFArrayGetValues` inside `CFArray` (#12537)

This requires less native calls (one for all values) than the current
code (one per value inside the array).

However the performance enhancement is not dramatic as the largest cost
is not in that code path (and the extra allocation undermine it too).
Still it's

I tried a few other variations (e.g. loop over a stackalloc'ed buffer)
but they did not perform better and the code was more complex.

Note: the NSArray* code/tests are identical between both sets (and the
mean results are also closer)

Actual Implementation

|                        Method |          name |              value |              Mean |              Error |          StdDev |
|------------------------------ |-------------- |------------------- |------------------:|-------------------:|----------------:|
| CFArray_StringArrayFromHandle |         empty |                 () |        124.856 ns |         13.1140 ns |       0.7188 ns |
| NSArray_StringArrayFromHandle |         empty |                 () |      1,438.752 ns |        120.3745 ns |       6.5981 ns |
|       CFArray_ArrayFromHandle |         empty |                 () |        124.727 ns |         21.3443 ns |       1.1700 ns |
|       NSArray_ArrayFromHandle |         empty |                 () |      1,433.047 ns |        164.6988 ns |       9.0277 ns |
| CFArray_StringArrayFromHandle |           few |  (  (...).") [108] |      1,818.681 ns |        457.2275 ns |      25.0622 ns |
| NSArray_StringArrayFromHandle |           few |  (  (...).") [108] |      8,636.130 ns |      2,505.6452 ns |     137.3429 ns |
|       CFArray_ArrayFromHandle |           few |  (  (...).") [108] |      4,105.635 ns |      1,151.4048 ns |      63.1124 ns |
|       NSArray_ArrayFromHandle |           few |  (  (...).") [108] |      8,769.657 ns |      1,946.9970 ns |     106.7215 ns |
| CFArray_StringArrayFromHandle | large_mutable |  ((...)) [8419330] | 20,103,343.750 ns | 13,762,095.1905 ns | 754,346.8755 ns |
| NSArray_StringArrayFromHandle | large_mutable |  ((...)) [8419330] | 24,256,992.708 ns |  9,357,034.9200 ns | 512,890.6579 ns |
|       CFArray_ArrayFromHandle | large_mutable |  ((...)) [8419330] |  3,888,197.135 ns |    395,545.6860 ns |  21,681.1938 ns |
|       NSArray_ArrayFromHandle | large_mutable |  ((...)) [8419330] |  7,326,581.510 ns |    622,485.7999 ns |  34,120.5472 ns |
| CFArray_StringArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    194,289.884 ns |     27,658.0701 ns |   1,516.0322 ns |
| NSArray_StringArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    549,336.410 ns |     98,695.3827 ns |   5,409.8270 ns |
|       CFArray_ArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    222,837.679 ns |     38,823.2619 ns |   2,128.0340 ns |
|       NSArray_ArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    441,116.243 ns |     46,578.8698 ns |   2,553.1450 ns |
| CFArray_StringArrayFromHandle |          null |                  ? |          4.568 ns |          0.8750 ns |       0.0480 ns |
| NSArray_StringArrayFromHandle |          null |                  ? |          4.728 ns |          1.0713 ns |       0.0587 ns |
|       CFArray_ArrayFromHandle |          null |                  ? |          4.818 ns |          1.0416 ns |       0.0571 ns |
|       NSArray_ArrayFromHandle |          null |                  ? |          4.793 ns |          0.5478 ns |       0.0300 ns |
| CFArray_StringArrayFromHandle |           one |            (    1) |        682.173 ns |         83.0811 ns |       4.5540 ns |
| NSArray_StringArrayFromHandle |           one |            (    1) |      3,409.570 ns |      1,407.8715 ns |      77.1702 ns |
|       CFArray_ArrayFromHandle |           one |            (    1) |      1,008.268 ns |        340.9156 ns |      18.6867 ns |
|       NSArray_ArrayFromHandle |           one |            (    1) |      3,174.222 ns |        412.6530 ns |      22.6189 ns |
| CFArray_StringArrayFromHandle | small_mutable | (   (...)9e") [54] |        909.008 ns |        271.9224 ns |      14.9050 ns |
| NSArray_StringArrayFromHandle | small_mutable | (   (...)9e") [54] |      5,017.722 ns |         66.9051 ns |       3.6673 ns |
|       CFArray_ArrayFromHandle | small_mutable | (   (...)9e") [54] |      1,900.176 ns |        131.5765 ns |       7.2121 ns |
|       NSArray_ArrayFromHandle | small_mutable | (   (...)9e") [54] |      4,913.822 ns |      1,264.8949 ns |      69.3332 ns |

New Implementation

|                        Method |          name |              value |              Mean |             Error |          StdDev |
|------------------------------ |-------------- |------------------- |------------------:|------------------:|----------------:|
| CFArray_StringArrayFromHandle |         empty |                 () |        112.335 ns |         0.7103 ns |       0.0389 ns |
| NSArray_StringArrayFromHandle |         empty |                 () |      1,497.473 ns |     1,508.7464 ns |      82.6995 ns |
|       CFArray_ArrayFromHandle |         empty |                 () |        114.670 ns |         3.7721 ns |       0.2068 ns |
|       NSArray_ArrayFromHandle |         empty |                 () |      1,486.298 ns |       767.5367 ns |      42.0713 ns |
| CFArray_StringArrayFromHandle |           few |  (  (...).") [108] |      1,526.398 ns |       205.6683 ns |      11.2734 ns |
| NSArray_StringArrayFromHandle |           few |  (  (...).") [108] |      8,503.264 ns |       981.4524 ns |      53.7967 ns |
|       CFArray_ArrayFromHandle |           few |  (  (...).") [108] |      3,814.918 ns |     1,868.8035 ns |     102.4354 ns |
|       NSArray_ArrayFromHandle |           few |  (  (...).") [108] |      8,628.285 ns |     2,416.9366 ns |     132.4805 ns |
| CFArray_StringArrayFromHandle | large_mutable |  ((...)) [8419330] | 16,351,876.042 ns | 3,691,993.1957 ns | 202,370.6051 ns |
| NSArray_StringArrayFromHandle | large_mutable |  ((...)) [8419330] | 24,589,916.667 ns | 2,800,427.9107 ns | 153,500.9034 ns |
|       CFArray_ArrayFromHandle | large_mutable |  ((...)) [8419330] |  3,390,725.260 ns | 2,950,426.1305 ns | 161,722.8120 ns |
|       NSArray_ArrayFromHandle | large_mutable |  ((...)) [8419330] |  7,296,976.823 ns | 4,376,785.2488 ns | 239,906.3683 ns |
| CFArray_StringArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    167,120.182 ns |    14,246.9472 ns |     780.9232 ns |
| NSArray_StringArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    554,940.788 ns |   116,685.5145 ns |   6,395.9268 ns |
|       CFArray_ArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    192,447.498 ns |    15,447.8562 ns |     846.7491 ns |
|       NSArray_ArrayFromHandle |   lot_mutable |  ( (...)") [34427] |    431,045.597 ns |    42,844.2650 ns |   2,348.4387 ns |
| CFArray_StringArrayFromHandle |          null |                  ? |          5.157 ns |         4.3360 ns |       0.2377 ns |
| NSArray_StringArrayFromHandle |          null |                  ? |          5.040 ns |         0.0694 ns |       0.0038 ns |
|       CFArray_ArrayFromHandle |          null |                  ? |          5.514 ns |         0.3849 ns |       0.0211 ns |
|       NSArray_ArrayFromHandle |          null |                  ? |          5.373 ns |         0.8358 ns |       0.0458 ns |
| CFArray_StringArrayFromHandle |           one |            (    1) |        730.162 ns |        65.9006 ns |       3.6122 ns |
| NSArray_StringArrayFromHandle |           one |            (    1) |      3,421.896 ns |       315.0152 ns |      17.2670 ns |
|       CFArray_ArrayFromHandle |           one |            (    1) |      1,052.039 ns |       295.2081 ns |      16.1814 ns |
|       NSArray_ArrayFromHandle |           one |            (    1) |      3,150.829 ns |       375.5461 ns |      20.5849 ns |
| CFArray_StringArrayFromHandle | small_mutable | (   (...)9e") [54] |        845.227 ns |        41.1244 ns |       2.2542 ns |
| NSArray_StringArrayFromHandle | small_mutable | (   (...)9e") [54] |      5,062.850 ns |     1,601.6880 ns |      87.7939 ns |
|       CFArray_ArrayFromHandle | small_mutable | (   (...)9e") [54] |      1,824.390 ns |       261.6341 ns |      14.3410 ns |
|       NSArray_ArrayFromHandle | small_mutable | (   (...)9e") [54] |      4,871.647 ns |       165.4350 ns |       9.0680 ns |

Fix https://github.com/xamarin/xamarin-macios/issues/12375
This commit is contained in:
Sebastien Pouliot 2021-08-26 16:37:31 -04:00 коммит произвёл GitHub
Родитель 5bab0dad01
Коммит 1394420fdc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 40 добавлений и 27 удалений

Просмотреть файл

@ -129,43 +129,51 @@ namespace CoreFoundation {
internal CFArray Clone () => new CFArray (CFArrayCreateCopy (IntPtr.Zero, GetCheckedHandle ()), true);
[DllImport (Constants.CoreFoundationLibrary)]
internal extern static void CFArrayGetValues (/* CFArrayRef */ IntPtr theArray, CFRange range, /* const void ** */ IntPtr values);
// identical signature to NSArray API
static public string?[]? StringArrayFromHandle (IntPtr handle)
static unsafe public string?[]? StringArrayFromHandle (IntPtr handle)
{
if (handle == IntPtr.Zero)
return null;
var c = CFArrayGetCount (handle);
var c = (int) CFArrayGetCount (handle);
if (c == 0)
return Array.Empty<string> ();
var buffer = c <= 256 ? stackalloc IntPtr [c] : new IntPtr [c];
fixed (void* ptr = buffer)
CFArrayGetValues (handle, new CFRange (0, c), (IntPtr) ptr);
string?[] ret = new string [c];
for (nint i = 0; i < c; i++)
ret [i] = CFString.FromHandle (CFArrayGetValueAtIndex (handle, i));
for (var i = 0; i < c; i++)
ret [i] = CFString.FromHandle (buffer [i]);
return ret;
}
static T? UnsafeGetItem<T> (IntPtr handle, nint index) where T : class, INativeObject
{
var val = CFArrayGetValueAtIndex (handle, index);
// Native code could return a CFArray with kCFNull inside its elements
// and they should be valid for things like T : NSDate so we handle
// them as just null values inside the array
if (val == CFNullHandle)
return null;
return Runtime.GetINativeObject<T> (val, false);
}
// identical signature to NSArray API
static public T?[]? ArrayFromHandle<T> (IntPtr handle) where T : class, INativeObject
{
if (handle == IntPtr.Zero)
return null;
var c = CFArrayGetCount (handle);
T?[] ret = new T [c];
var c = (int) CFArrayGetCount (handle);
if (c == 0)
return Array.Empty<T> ();
for (nint i = 0; i < c; i++)
ret [i] = UnsafeGetItem<T> (handle, i);
var buffer = c <= 256 ? stackalloc IntPtr [c] : new IntPtr [c];
unsafe {
fixed (void* ptr = buffer)
CFArrayGetValues (handle, new CFRange (0, c), (IntPtr) ptr);
}
T?[] ret = new T [c];
for (var i = 0; i < c; i++) {
var val = buffer [i];
if (val != CFNullHandle)
ret [i] = Runtime.GetINativeObject<T> (val, false);
}
return ret;
}
}

Просмотреть файл

@ -92,9 +92,15 @@ namespace PerfTest {
public IEnumerable<object []> ArraysOfStrings ()
{
yield return new object [] { "null", null };
yield return new object [] { "empty", new NSArray () };
yield return new object [] { "one", NSArray.FromStrings ("1") };
yield return new object [] { "few", NSArray.FromStrings ("Bonjour", "Québec", "汉语 漢語", "I'm feeling 🤪 tonight.") };
yield return new object [] { "mutable", new NSMutableArray<NSString> (new NSString ("Québec"), new NSString ("汉语 漢語")) };
yield return new object [] { "small_mutable", new NSMutableArray<NSString> (new NSString ("Québec"), new NSString ("汉语 漢語")) };
var lot = new NSMutableArray ();
for (int i = 0; i < 255; i++) // used to fit under the stackalloc limit of the new implementation
lot.Add (new NSString (new string ('!', i) ));
yield return new object [] { "lot_mutable", lot };
var large = new NSMutableArray ();
for (int i = 0; i < 4096; i++)
large.Add (new NSString (new string ('#', i) ));
@ -108,7 +114,7 @@ namespace PerfTest {
[ArgumentsSource (nameof (ArraysOfStrings))]
public void CFArray_StringArrayFromHandle (string name, NSArray value)
{
CFArray.StringArrayFromHandle (value.Handle);
CFArray.StringArrayFromHandle (value.GetHandle ());
}
/*
@ -118,7 +124,7 @@ namespace PerfTest {
[ArgumentsSource (nameof (ArraysOfStrings))]
public void NSArray_StringArrayFromHandle (string name, NSArray value)
{
NSArray.StringArrayFromHandle (value.Handle);
NSArray.StringArrayFromHandle (value.GetHandle ());
}
/*
@ -128,7 +134,7 @@ namespace PerfTest {
[ArgumentsSource (nameof (ArraysOfStrings))]
public void CFArray_ArrayFromHandle (string name, NSArray value)
{
CFArray.ArrayFromHandle<NSString> (value.Handle);
CFArray.ArrayFromHandle<NSString> (value.GetHandle ());
}
/*
@ -138,7 +144,7 @@ namespace PerfTest {
[ArgumentsSource (nameof (ArraysOfStrings))]
public void NSArray_ArrayFromHandle (string name, NSArray value)
{
NSArray.ArrayFromHandle<NSString> (value.Handle);
NSArray.ArrayFromHandle<NSString> (value.GetHandle ());
}
}
}

Просмотреть файл

@ -347,7 +347,6 @@
!missing-pinvoke! CFArrayGetCountOfValue is not bound
!missing-pinvoke! CFArrayGetFirstIndexOfValue is not bound
!missing-pinvoke! CFArrayGetLastIndexOfValue is not bound
!missing-pinvoke! CFArrayGetValues is not bound
!missing-pinvoke! CFArrayInsertValueAtIndex is not bound
!missing-pinvoke! CFArrayRemoveAllValues is not bound
!missing-pinvoke! CFArrayRemoveValueAtIndex is not bound