Rolf Bjarne Kvinge
36af029204
Change all null checking expressions to use 'is' and 'is not'. ( #18176 )
...
Change all null checking expressions to use 'is null' and 'is not null'
instead of '== null' and '!= null'.
This was mostly done with sed, so code can probably be improved in many
other ways with manual inspection, but that will come over time.
Also add code to the autoformat script to automatically fix these issues in the future.
2023-05-05 17:52:19 +02:00
Rolf Bjarne Kvinge
1c80823582
[autoformat] Add all of tests/. ( #16857 )
2022-12-05 08:23:34 +01:00
Rolf Bjarne Kvinge
a1d0b6eba9
Make our local .NET the default .NET for the build. ( #15086 )
...
Make our local .NET the default .NET (in the root's global.json), and then if
a directory wants to use the system .NET, then that directory would have to
opt-in (using its own global.json).
This way we don't have to copy global.json/NuGet.config files around to run
tests with the correct .NET setup.
2022-06-07 10:11:02 +02:00
Sebastien Pouliot
d869a6868a
[corefoundation] Cache `kCFNull` to avoid native calls ( #15146 )
2022-05-30 12:38:27 +02:00
Rolf Bjarne Kvinge
a46afd0147
[tests] Use 'BundledNETCoreAppTargetFrameworkVersion' to specify the .NET version in the project files. ( #14666 )
...
This way we don't have to update all these files when moving to .NET 7.
2022-04-06 20:58:20 +02:00
Rolf Bjarne Kvinge
08978fa4b1
[dotnet] Rename hardcoded 'net6.0' to use a 'DOTNET_TFM' variable instead. ( #14524 )
...
This makes things easier for .NET 7.
2022-04-01 13:48:09 +02:00
Rolf Bjarne Kvinge
e369f466f7
[dotnet] Rename the DOTNET6 make variable to DOTNET. ( #14441 )
...
This is the follow-up to where the DOTNET variable was renamed SYSTEM_DOTNET.
2022-03-21 15:56:57 +01:00
Sebastien Pouliot
e82711f73e
[corefoundation] Optimize `CFString` creation ( #12736 )
...
* Add fast path if string is empty (`length == 0`)
* Add fast path (less allocations) for short string (`stackalloc`)
* Use `Marshal.AllocHGlobal`
* we were the only consumer of `Marshal.AllocCoTaskMem` and `FreeCoTaskMem` (inside most apps) so those symbols can now be removed
* `Marshal.AllocCoTaskMem` simply calls `AllocHGlobal` (with some extra casts) so it does not really have any other impact
* Add a few `CFString.FromHandle`` performance tests
To cover cases for
* `nil` which returns `null`
* `128` characters, which is the limit for `stackalloc`
* `129` characters, which is just over the limit for `stackalloc`
**Expectations**
* `empty` should be faster, since it now returns earlier
* `short_*` and `stackalloc_limit` should be a bit faster as they use `stackalloc`
* `allochglobal` and `long_string` should be (nearly) identical
**Before**
| Method | name | Mean | Error | StdDev |
|-------------------- |----------------- |-------------:|------------:|-----------:|
| CFString_FromString | allochglobal | 547.765 ns | 245.5782 ns | 13.4610 ns |
| CFString_FromString | empty | 377.983 ns | 6.7926 ns | 0.3723 ns |
| CFString_FromString | long_string | 5,480.664 ns | 264.9473 ns | 14.5227 ns |
| CFString_FromString | nil | 4.848 ns | 0.0412 ns | 0.0023 ns |
| CFString_FromString | short_7bits | 442.096 ns | 30.4907 ns | 1.6713 ns |
| CFString_FromString | short_accent | 221.221 ns | 2.2069 ns | 0.1210 ns |
| CFString_FromString | short_emoji | 221.870 ns | 2.4471 ns | 0.1341 ns |
| CFString_FromString | short_unicode | 217.954 ns | 1.8771 ns | 0.1029 ns |
| CFString_FromString | stackalloc_limit | 484.019 ns | 6.3940 ns | 0.3505 ns |
**After**
| Method | name | Mean | Error | StdDev |
|-------------------- |----------------- |-------------:|------------:|-----------:|
| CFString_FromString | allochglobal | 554.028 ns | 317.1811 ns | 17.3858 ns |
| CFString_FromString | empty | 95.442 ns | 7.6434 ns | 0.4190 ns |
| CFString_FromString | long_string | 5,468.565 ns | 251.2252 ns | 13.7705 ns |
| CFString_FromString | nil | 3.299 ns | 0.0229 ns | 0.0013 ns |
| CFString_FromString | short_7bits | 352.567 ns | 25.7539 ns | 1.4117 ns |
| CFString_FromString | short_accent | 221.132 ns | 0.9760 ns | 0.0535 ns |
| CFString_FromString | short_emoji | 222.557 ns | 4.5978 ns | 0.2520 ns |
| CFString_FromString | short_unicode | 216.375 ns | 5.1619 ns | 0.2829 ns |
| CFString_FromString | stackalloc_limit | 427.663 ns | 77.9610 ns | 4.2733 ns |
2022-02-23 20:49:10 +01:00
Sebastien Pouliot
2691791ad2
[corefoundation] Optimize `CFArray` ( #12740 )
...
The added test case has not shown any major performance enhancement,
anything (if) is lost in the noise.
Beside the performance the change has two small size advantages
* `Create` does not call another method (less metadata)
* `GetCount` is now the p/invoke `CFArrayGetCount`
Before
// * Summary *
BenchmarkDotNet=v0.12.1.1528-nightly, OS=macOS Big Sur 11.5.2 (20G95) [Darwin 20.6.0]
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
[Host] : Mono 6.12.0 (2020-02/3cf59ad33da), X64
Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=3
LaunchCount=1 WarmupCount=3
| Method | Length | Mean | Error | StdDev |
|------- |------- |------------:|------------:|----------:|
| Create | 0 | 255.4 ns | 53.68 ns | 2.94 ns |
| Create | 1 | 343.0 ns | 8.55 ns | 0.47 ns |
| Create | 16 | 443.8 ns | 30.54 ns | 1.67 ns |
| Create | 256 | 1,898.4 ns | 246.80 ns | 13.53 ns |
| Create | 4096 | 40,503.2 ns | 5,395.81 ns | 295.76 ns |
// * Legends *
Length : Value of the 'Length' parameter
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
1 ns : 1 Nanosecond (0.000000001 sec)
// ***** BenchmarkRunner: End *****
Global total time: 00:00:33 (33.26 sec), executed benchmarks: 5
After
// * Summary *
BenchmarkDotNet=v0.12.1.1528-nightly, OS=macOS Big Sur 11.5.2 (20G95) [Darwin 20.6.0]
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
[Host] : Mono 6.12.0 (2020-02/3cf59ad33da), X64
Job=InProcess Toolchain=InProcessEmitToolchain IterationCount=3
LaunchCount=1 WarmupCount=3
| Method | Length | Mean | Error | StdDev |
|------- |------- |------------:|------------:|---------:|
| Create | 0 | 246.3 ns | 55.49 ns | 3.04 ns |
| Create | 1 | 341.1 ns | 18.13 ns | 0.99 ns |
| Create | 16 | 425.4 ns | 34.97 ns | 1.92 ns |
| Create | 256 | 1,741.1 ns | 298.87 ns | 16.38 ns |
| Create | 4096 | 40,333.6 ns | 1,646.70 ns | 90.26 ns |
// * Legends *
Length : Value of the 'Length' parameter
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
1 ns : 1 Nanosecond (0.000000001 sec)
// ***** BenchmarkRunner: End *****
Global total time: 00:00:31 (31.68 sec), executed benchmarks: 5
2021-09-16 08:39:16 +02:00
Rolf Bjarne Kvinge
f75a7b5074
[tests] Add perf test for calling Marshal.AllocHGlobal/FreeHGlobal. ( #12696 )
...
Also add a 'SupportedOSPlatformVersion' value to the .NET perftest project
file, to cope with recent changes in our .NET support.
Ref: https://github.com/dotnet/runtime/issues/58939 .
2021-09-13 16:14:37 +02:00
Sebastien Pouliot
1394420fdc
[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
2021-08-26 16:37:31 -04:00
Sebastien Pouliot
2b1a5c12f4
[generator] Use new CFArray.ArrayFromHandle instead of the existing NSArray API ( #12415 )
...
Similar to https://github.com/xamarin/xamarin-macios/pull/12368
p/invoke are faster than selectors, so we use them inside generated
bindings and suggest (thru `[Obsolete]`) the use of `CFArray` in manual
bindings and 3rd-party code.
| Method | name | value | Mean | Error | StdDev |
|------------------------------ |-------------- |-------------------- |----------------:|-----------------:|----------------:|
| CFArray_ArrayFromHandle | empty | () | 120.1 ns | 31.37 ns | 1.72 ns |
| NSArray_ArrayFromHandle | empty | () | 1,433.4 ns | 168.78 ns | 9.25 ns |
| CFArray_ArrayFromHandle | few | ( (...).") [108] | 4,113.2 ns | 290.55 ns | 15.93 ns |
| NSArray_ArrayFromHandle | few | ( (...).") [108] | 8,617.3 ns | 1,958.88 ns | 107.37 ns |
| CFArray_ArrayFromHandle | large_mutable | ((...)) [8419330] | 4,045,002.6 ns | 438,639.47 ns | 24,043.31 ns |
| NSArray_ArrayFromHandle | large_mutable | ((...)) [8419330] | 7,138,524.0 ns | 1,147,635.42 ns | 62,905.77 ns |
| CFArray_ArrayFromHandle | mutable | ( (...)9e") [54] | 1,974.9 ns | 576.90 ns | 31.62 ns |
| NSArray_ArrayFromHandle | mutable | ( (...)9e") [54] | 4,882.9 ns | 269.60 ns | 14.78 ns |
Note: the use of `CFArrayGetValues` as suggested before (see https://github.com/xamarin/xamarin-macios/issues/12375 )
would also apply here - but that needs a bit more fine-tuning (to offset
the extra allocation) so it will be handled by a future PR.
# Side Effect Trivia
Along with previous work this means that a _minimal_ app can now trim both `NSArray` and `NSNull` (managed code) and their need to be registered at startup.
```diff
--- a.cs 2021-08-12 08:18:15.000000000 -0400
+++ b.cs 2021-08-12 08:19:35.000000000 -0400
@@ -2111,6 +2111,11 @@
{
public static readonly IntPtr Handle = Dlfcn._dlopen("/usr/lib/libSystem.dylib", 0);
}
+
+ public static class CoreFoundation
+ {
+ public static readonly IntPtr Handle = Dlfcn._dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", 0);
+ }
}
public static class Dlfcn
{
@@ -2119,6 +2124,16 @@
[DllImport("/usr/lib/libSystem.dylib")]
public static extern IntPtr dlsym(IntPtr P_0, string P_1);
+
+ public static IntPtr GetIntPtr(IntPtr P_0, string P_1)
+ {
+ IntPtr intPtr = dlsym(P_0, P_1);
+ if (intPtr == IntPtr.Zero)
+ {
+ return IntPtr.Zero;
+ }
+ return Marshal.ReadIntPtr(intPtr);
+ }
}
internal static class ErrorHelper
{
@@ -3114,9 +3129,6 @@
public static extern void void_objc_msgSend(IntPtr P_0, IntPtr P_1);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
- public static extern IntPtr IntPtr_objc_msgSend_nuint(IntPtr P_0, IntPtr P_1, nuint P_2);
-
- [DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
public static extern CGRect CGRect_objc_msgSend(IntPtr P_0, IntPtr P_1);
[DllImport("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
@@ -3590,54 +3602,6 @@
return stringBuilder.ToString();
}
}
- [Register("NSArray", true)]
- public sealed class NSArray : NSObject, INativeObject, IDisposable
- {
- private static readonly IntPtr class_ptr = Class.GetHandle("NSArray");
-
- public sealed override IntPtr ClassHandle => class_ptr;
-
- internal static nuint GetCount(IntPtr P_0)
- {
- return Messaging.nuint_objc_msgSend(P_0, Selector.GetHandle("count"));
- }
-
- internal static IntPtr GetAtIndex(IntPtr P_0, nuint P_1)
- {
- return Messaging.IntPtr_objc_msgSend_nuint(P_0, Selector.GetHandle("objectAtIndex:"), P_1);
- }
-
- public static T[] ArrayFromHandle<T>(IntPtr P_0) where T : class, INativeObject
- {
- if (P_0 == IntPtr.Zero)
- {
- return null;
- }
- nuint count = GetCount(P_0);
- T[] array = new T[(ulong)count];
- for (uint num = 0u; num < count; num++)
- {
- array[num] = UnsafeGetItem<T>(P_0, num);
- }
- return array;
- }
-
- private static T UnsafeGetItem<T>(IntPtr P_0, nuint P_1) where T : class, INativeObject
- {
- IntPtr atIndex = GetAtIndex(P_0, P_1);
- if (atIndex == NSNull.Null.Handle)
- {
- return null;
- }
- return Runtime.GetINativeObject<T>(atIndex, false);
- }
-
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- protected internal NSArray(IntPtr P_0)
- : base(P_0)
- {
- }
- }
[DefaultMember("Item")]
[Register("NSDictionary", true)]
public sealed class NSDictionary : NSObject, IDictionary, ICollection<KeyValuePair<NSObject, NSObject>>, IEnumerable<KeyValuePair<NSObject, NSObject>>, INativeObject, IDisposable
@@ -3665,7 +3629,7 @@
{
using (new NSAutoreleasePool())
{
- return NSArray.ArrayFromHandle<NSObject>(Messaging.IntPtr_objc_msgSend(base.Handle, Selector.GetHandle("allKeys")));
+ return CFArray.ArrayFromHandle<NSObject>(Messaging.IntPtr_objc_msgSend(base.Handle, Selector.GetHandle("allKeys")));
}
}
}
@@ -3717,43 +3681,6 @@
return Runtime.GetNSObject(Messaging.IntPtr_objc_msgSend_IntPtr(base.Handle, Selector.GetHandle("objectForKey:"), nonNullHandle));
}
}
- [Register("NSNull", true)]
- public sealed class NSNull : NSObject, INativeObject, IDisposable
- {
- private static NSNull _null;
-
- private static readonly IntPtr class_ptr = Class.GetHandle("NSNull");
-
- public static NSNull Null
- {
- get
- {
- if (_null == null)
- {
- _null = _Null;
- }
- return _null;
- }
- }
-
- public sealed override IntPtr ClassHandle => class_ptr;
-
- internal static NSNull _Null
- {
- [System.Runtime.CompilerServices.NullableContext(1)]
- [Export("null")]
- get
- {
- return Runtime.GetNSObject<NSNull>(Messaging.IntPtr_objc_msgSend(class_ptr, Selector.GetHandle("null")));
- }
- }
-
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- protected internal NSNull(IntPtr P_0)
- : base(P_0)
- {
- }
- }
[Register("NSRunLoop", true)]
public sealed class NSRunLoop : NSObject
{
@@ -4702,26 +4629,11 @@
return (int)P_0.v;
}
- public static implicit operator nuint(uint P_0)
- {
- return new nuint(P_0);
- }
-
public static explicit operator nuint(ulong P_0)
{
return new nuint(P_0);
}
- public static implicit operator ulong(nuint P_0)
- {
- return P_0.v;
- }
-
- public static bool operator <(nuint P_0, nuint P_1)
- {
- return P_0.v < P_1.v;
- }
-
public int CompareTo(nuint P_0)
{
return v.CompareTo(P_0.v);
@@ -4867,6 +4779,8 @@
{
public sealed class CFArray : NativeObject
{
+ internal static IntPtr CFNullHandle => Dlfcn.GetIntPtr(Libraries.CoreFoundation.Handle, "kCFNull");
+
internal CFArray(IntPtr P_0)
: base(P_0, false)
{
@@ -4897,6 +4811,31 @@
}
return array;
}
+
+ private static T? UnsafeGetItem<T>(IntPtr P_0, nint P_1) where T : class, INativeObject
+ {
+ IntPtr intPtr = CFArrayGetValueAtIndex(P_0, P_1);
+ if (intPtr == CFNullHandle)
+ {
+ return null;
+ }
+ return Runtime.GetINativeObject<T>(intPtr, false);
+ }
+
+ public static T?[]? ArrayFromHandle<T>(IntPtr P_0) where T : class, INativeObject
+ {
+ if (P_0 == IntPtr.Zero)
+ {
+ return null;
+ }
+ nint nint = CFArrayGetCount(P_0);
+ T[] array = new T[(long)nint];
+ for (nint nint2 = 0; nint2 < nint; ++nint2)
+ {
+ array[(long)nint2] = UnsafeGetItem<T>(P_0, nint2);
+ }
+ return array;
+ }
}
public struct CFRange
{
```
2021-08-12 14:19:25 -04:00
Rolf Bjarne Kvinge
2972e1b715
Fix some whitespace issues in various files. ( #12399 )
...
* Remove BOM
* Add EOL at end of file.
2021-08-11 10:06:46 +02:00
Sebastien Pouliot
22ccf2e81b
[generator] Use new `CFArray.StringArrayFromHandle` instead of the existing `NSArray` API ( #12368 )
...
This is another example that p/invokes are much faster than calling
selectors.
Beside the generator the manual bindings were updated to use the newer
API and the old one was decorated as `[Obsolete]`.
| Method | name | value | Mean | Error | StdDev |
|------------------------------ |-------------- |--------------------- |----------------:|----------------:|--------------:|
| CFArray_StringArrayFromHandle | empty | () | 123.9 ns | 68.92 ns | 3.78 ns |
| NSArray_StringArrayFromHandle | empty | () | 1,422.6 ns | 25.83 ns | 1.42 ns |
| CFArray_StringArrayFromHandle | few | ( (...).") [108] | 1,885.2 ns | 46.37 ns | 2.54 ns |
| NSArray_StringArrayFromHandle | few | ( (...).") [108] | 8,530.0 ns | 594.40 ns | 32.58 ns |
| CFArray_StringArrayFromHandle | large_mutable | ((...)) [8419330] | 15,821,101.0 ns | 4,803,631.19 ns | 263,303.23 ns |
| NSArray_StringArrayFromHandle | large_mutable | ((...)) [8419330] | 22,823,871.9 ns | 6,589,380.43 ns | 361,186.18 ns |
| CFArray_StringArrayFromHandle | mutable | ( (...)9e") [54] | 867.4 ns | 59.23 ns | 3.25 ns |
| NSArray_StringArrayFromHandle | mutable | ( (...)9e") [54] | 4,939.6 ns | 203.28 ns | 11.14 ns |
note: `NSArray.StringArrayFromHandle` was already using (p/invoke-based)
`CFString.FromHandle` instead of (selector-based) `NSString.FromHandle`
to create the managed `string` instances inside the `string[]`.
2021-08-06 09:17:48 -04:00
Sebastien Pouliot
0649960dc3
Use `CFString` instead of `NSString` to get a handle from a (.net) `System.String` ( #11946 )
...
This is possible because both types are toll-free bridged [0].
How ?
* Renamed `CFString.LowLevelCreate` to `CreateNative` so it match `NSString` API
* Make it public so it can be used for generated/3rd party bindings
* Added a _safe_ `CFString.ReleaseNative` matching `NSString` API
* Update generator to use the new API (instead of the NSString version)
* Update manual bindings (using older API) to use the new API (few in PrintCore)
Why ?
It's no secret that p/invoke (C) are faster than calling a selector
(ObjC). In most cases we do not have a choice what to call... but in a
few, but commonly used, cases we can pick the fastest call.
In this case the difference is larger than the previous case [1] since there's
two call (create and release) involved.
| Method | name | value | Mean | Error | StdDev |
|----------------------- |-------------- |--------------------- |------------:|------------:|----------:|
| NSString_CreateRelease | empty | | 4,936.1 ns | 349.08 ns | 19.13 ns |
| CFString_CreateRelease | empty | | 243.1 ns | 10.30 ns | 0.56 ns |
| NSString_CreateRelease | long_string | ????(...)???? [4096] | 8,270.3 ns | 837.09 ns | 45.88 ns |
| CFString_CreateRelease | long_string | ????(...)???? [4096] | 3,212.4 ns | 213.73 ns | 11.72 ns |
| NSString_CreateRelease | short_7bits | Bonjour | 4,858.5 ns | 1,671.67 ns | 91.63 ns |
| CFString_CreateRelease | short_7bits | Bonjour | 242.5 ns | 18.36 ns | 1.01 ns |
| NSString_CreateRelease | short_accent | Québec | 4,990.1 ns | 343.96 ns | 18.85 ns |
| CFString_CreateRelease | short_accent | Québec | 377.6 ns | 15.96 ns | 0.87 ns |
| NSString_CreateRelease | short_emoji | I'm f(...)ight. [23] | 5,080.2 ns | 794.29 ns | 43.54 ns |
| CFString_CreateRelease | short_emoji | I'm f(...)ight. [23] | 391.2 ns | 8.60 ns | 0.47 ns |
| NSString_CreateRelease | short_unicode | 汉语 漢語 | 4,980.4 ns | 272.41 ns | 14.93 ns |
| CFString_CreateRelease | short_unicode | 汉语 漢語 | 376.4 ns | 15.98 ns | 0.88 ns |
Also simplified/updated previous tests as suggested in [1].
| Method | name | value | Mean | Error | StdDev |
|----------------------- |-------------- |--------------------- |------------:|------------:|----------:|
| NSString_FromString | empty | 140735350713040 | 2,252.2 ns | 591.63 ns | 32.43 ns |
| CFString_FromString | empty | 140735350713040 | 408.4 ns | 47.56 ns | 2.61 ns |
| NSString_FromString | long_string | 140295885995872 | 14,887.2 ns | 183.67 ns | 10.07 ns |
| CFString_FromString | long_string | 140295885995872 | 7,418.7 ns | 1,844.42 ns | 101.10 ns |
| NSString_FromString | short_7bits | 1292713981587835779 | 2,259.8 ns | 210.17 ns | 11.52 ns |
| CFString_FromString | short_7bits | 1292713981587835779 | 453.0 ns | 122.77 ns | 6.73 ns |
| NSString_FromString | short_accent | 140295885995776 | 2,448.2 ns | 97.43 ns | 5.34 ns |
| CFString_FromString | short_accent | 140295885995776 | 256.8 ns | 75.81 ns | 4.16 ns |
| NSString_FromString | short_emoji | 140296423669120 | 2,531.6 ns | 199.02 ns | 10.91 ns |
| CFString_FromString | short_emoji | 140296423669120 | 259.0 ns | 21.32 ns | 1.17 ns |
| NSString_FromString | short_unicode | 140295885992304 | 2,426.2 ns | 339.06 ns | 18.58 ns |
| CFString_FromString | short_unicode | 140295885992304 | 260.6 ns | 97.03 ns | 5.32 ns |
[1] https://github.com/xamarin/xamarin-macios/pull/11809
2021-06-16 14:11:38 -04:00
Sebastien Pouliot
42a2c79ff8
Use `CFString` instead of `NSString` to get a (.net) `System.String` from an handle ( #11809 )
...
This is possible because both types are toll-free bridged [0].
How ?
* Renamed `CFString.FetchString` to `FromHandle` so it match `NSString` API
* Make it `public` so it can be used for generated/3rd party bindings
* Update generator to use the new API (instead of the `NSString` version)
* Update manual bindings to use the new API
Why ?
It's no secret that p/invoke (C) are faster than calling a selector
(ObjC). In most cases we do not have a choice what to call... but in a
few, but commonly used, cases we can pick the fastest call.
```
// * Summary *
BenchmarkDotNet=v0.12.1.1528-nightly, OS=macOS Big Sur 11.3.1 (20E241) [Darwin 20.4.0]
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
[Host] : Mono 6.12.0 (2020-02/c633fe92383), X64
Job=InProcess Toolchain=InProcessEmitToolchain InvocationCount=1
IterationCount=3 LaunchCount=1 WarmupCount=3
| Type | Method | UnrollFactor | ArraySize | Mean | Error | StdDev | Median |
|-------------------- |------------------------------------ |------------- |---------- |----------------:|-----------------:|----------------:|----------------:|
| TollFreeString | ReturnString_NSString | 16 | ? | 2,522.0 ns | 878.81 ns | 48.17 ns | 2,541.7 ns |
| TollFreeString | ReturnString_CFString | 16 | ? | 808.1 ns | 7.48 ns | 0.41 ns | 808.2 ns |
```
Next ?
The next step is to reduce other `NSString` selectors usage,
then `NSArray`...
[0] https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Toll-FreeBridgin/Toll-FreeBridgin.html
2021-06-05 10:33:47 -04:00
Rolf Bjarne Kvinge
c88c0b5283
[dotnet] Remove the .NET workload resolver workaround, it's not needed anymore. ( #11695 )
2021-05-27 07:30:46 +02:00
Rolf Bjarne Kvinge
d15f683500
[tests] Add a performance test using BenchmarkDotNet. ( #11298 )
2021-04-23 13:29:06 +02:00