-
Notifications
You must be signed in to change notification settings - Fork 533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add local date time offset monovm prop #7331
Add local date time offset monovm prop #7331
Conversation
@@ -5,9 +5,11 @@ using namespace xamarin::android::internal; | |||
MonoVMProperties::property_array MonoVMProperties::_property_keys { | |||
RUNTIME_IDENTIFIER_KEY, | |||
APP_CONTEXT_BASE_DIRECTORY_KEY, | |||
LOCAL_DATE_TIME_OFFSET_KEY, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope that one day we can replace this dynamically allocated property with a field in the MonoCoreRuntimeProperties
struct... It would make the code even faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much are we saving if we could leverage MonoCoreRuntimeProperties
struct over adding another property key/value pair?
There isn't any convenient way to put any of the MonoCoreRuntimeProperties
into AppContext on the runtime side. This is because none of the properties in MonoCoreRuntimeProperties
need to make it to the managed side. Another thing we would have to do is maybe differentiate between properties that need to make it to managed side or not to then specifically add into the AppContext.
Since populating AppContext is necessary for startup to finish, would the savings done by leveraging MonoCoreRuntimeProperties over adding a new key/value pair here be greater than the cost of needing to convert it to a string property in the runtime and any possible filtering before its added to the App Context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The biggest savings on XA side come from not having to convert an integer into a string and not duplicating that string with strdup
. These obviously aren't big savings, but it's something that needlessly consumes memory and processor time (since that string will be parsed back into integer a short while later). I wonder if AppContext can be populated lazily? That is, could there be code to retrieve the value(s) from MonoCoreRuntimeProperties
on demand (using an icall or p/invoke) instead of pre-populating it?
Also, looking at AppContext
docs, its GetData(string)
method returns an object?
- do we absolutely have to convert the integer retrieved here to string, then use Convert.ToInt32
after retrieving it from GetData
?
This just looks like a wasteful way of doing it... After all, we're running in the same process, so we can access variables (sort of) directly without having to proxy them via strings. There was a similar issue sometime ago with p/invoke overrides, where initially we had to convert a pointer to a hexadecimal string, pass it to the runtime which would then parse that string and cast it back into a pointer...
On desktop these string operations may not matter, on mobile it's an entirely different matter - every nanosecond saved counts.
@@ -25,7 +25,7 @@ | |||
<!-- Should correspond to the first value from `$(API_LEVELS)` in `build-tools/api-xml-adjuster/Makefile` --> | |||
<AndroidFirstFrameworkVersion Condition="'$(AndroidFirstFrameworkVersion)' == ''">v4.4</AndroidFirstFrameworkVersion> | |||
<AndroidFirstApiLevel Condition="'$(AndroidFirstApiLevel)' == ''">19</AndroidFirstApiLevel> | |||
<AndroidJavaRuntimeApiLevel Condition="'$(AndroidJavaRuntimeApiLevel)' == ''">21</AndroidJavaRuntimeApiLevel> | |||
<AndroidJavaRuntimeApiLevel Condition="'$(AndroidJavaRuntimeApiLevel)' == ''">26</AndroidJavaRuntimeApiLevel> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@grendello Is this api level bump ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is just for build time, so that the code which uses API 26 interfaces can be compiled. At runtime a check is made whether the API in question can be called. Bumping it here doesn't prevent running on older devices.
int localDateTimeOffset; | ||
|
||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { | ||
localDateTimeOffset = OffsetDateTime.now().getOffset().getTotalSeconds(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we tested this on an API-21 device or emulator? We may need to move this expression into a separate method; I am not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/azp run |
/azp run Xamarin.Android-PR |
Azure Pipelines successfully started running 1 pipeline(s). |
1 similar comment
Azure Pipelines successfully started running 1 pipeline(s). |
public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName, int apiLevel, String[] environmentVariables); | ||
public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, int localDateTimeOffset, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName, int apiLevel, String[] environmentVariables); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think adding a parameter to init
here is breaking the Designer tests -- they call this API directly.
Can we just pass a 0 or some default value through? I don't think it matters if the behavior for DateTime.Now
is off a little bit when rendering layouts in the designer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonathanpeppers Ah, I didnt realize that was the root of the failures on the unit tests. I'm not actually sure what the corresponding designer tests are, but do any actually leverage DateTimeOffset.Now
?
Whats the easiest way to confirm that adding a 0 or default parameter on the designer's init
call will fix the tests? If it does solve the problem, then would the correct order be to merge this pr knowing that it will break tests and then merge a pr on designer to add the parameter?
Or would it be better to make another init
like having both
public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName, int apiLevel, String[] environmentVariables);
public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, int localDateTimeOffset, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName, int apiLevel, String[] environmentVariables);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would guess that no designer tests use DateTimeOffset.Now
. It doesn't seem like something you would do in a custom UserControl
-like scenario.
But they do call this init()
method with a known set of parameters -- we can't change that or the designer will crash on startup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The designer tests look to be passing now, should everyone else take another review?
Yes, thanks for helping me figure out that |
[monodroid, java-runtime] Add local date time offset monovm prop (#7331)
Context: https://github.com/dotnet/runtime/pull/74459
Context: https://github.com/dotnet/runtime/issues/71004
Context: https://github.com/dotnet/runtime/pull/54845
dotnet/runtime#54845 added support TimeZoneInfo support for Android.
This was found to slow down initial use of `DateTimeOffset.Now` by
~277ms (dotnet/runtime#17004).
dotnet/runtime#74459 started reducing this startup overhead by adding
support for a new `System.TimeZoneInfo.LocalDateTimeOffset` property
on `AppContext`: if `System.TimeZoneInfo.LocalDateTimeOffset` is set,
it is used as the initial local DateTime Offset value.
Update `mono.android.MonoPackageManager.LoadApplication()` and
`libmono-android*.so` so that the
`System.TimeZoneInfo.LocalDateTimeOffset` AppContext property is set
during process startup. This can reduce `DateTimeOffset.Now` startup
overhead from ~277ms to ~50ms.
Note: `System.TimeZoneInfo.LocalDateTimeOffset` is fastest on
API-26+ (Android 8.0+) targets, as it uses
[`java.time.ZoneOffset.getTotalSeconds()`][0].
Note: `$(AndroidJavaRuntimeApiLevel)` is updated to API-26 so that
`ZoneOffset.getTotalSeconds()` can be used. Care should be taken
within `src/java-runtime` to ensure that Android API use is
appropriately protected behind runtime version checks.
[0]: https://developer.android.com/reference/kotlin/java/time/ZoneOffset#gettotalseconds |
Context: dotnet/runtime#74459 Context: dotnet/runtime#71004 Context: dotnet/runtime#54845 dotnet/runtime#54845 added support TimeZoneInfo support for Android. This was found to slow down initial use of `DateTimeOffset.Now` by ~277ms (dotnet/runtime#17004). dotnet/runtime#74459 started reducing this startup overhead by adding support for a new `System.TimeZoneInfo.LocalDateTimeOffset` property on `AppContext`: if `System.TimeZoneInfo.LocalDateTimeOffset` is set, it is used as the initial local DateTime Offset value. Update `mono.android.MonoPackageManager.LoadApplication()` and `libmono-android*.so` so that the `System.TimeZoneInfo.LocalDateTimeOffset` AppContext property is set during process startup. This can reduce `DateTimeOffset.Now` startup overhead from ~277ms to ~50ms. Note: `System.TimeZoneInfo.LocalDateTimeOffset` is fastest on API-26+ (Android 8.0+) targets, as it uses [`java.time.ZoneOffset.getTotalSeconds()`][0]. Note: `$(AndroidJavaRuntimeApiLevel)` is updated to API-26 so that `ZoneOffset.getTotalSeconds()` can be used. Care should be taken within `src/java-runtime` to ensure that Android API use is appropriately protected behind runtime version checks. [0]: https://developer.android.com/reference/kotlin/java/time/ZoneOffset#gettotalseconds
Context: dotnet/runtime#74459 Context: dotnet/runtime#71004 Context: dotnet/runtime#54845 dotnet/runtime#54845 added support TimeZoneInfo support for Android. This was found to slow down initial use of `DateTimeOffset.Now` by ~277ms (dotnet/runtime#17004). dotnet/runtime#74459 started reducing this startup overhead by adding support for a new `System.TimeZoneInfo.LocalDateTimeOffset` property on `AppContext`: if `System.TimeZoneInfo.LocalDateTimeOffset` is set, it is used as the initial local DateTime Offset value. Update `mono.android.MonoPackageManager.LoadApplication()` and `libmono-android*.so` so that the `System.TimeZoneInfo.LocalDateTimeOffset` AppContext property is set during process startup. This can reduce `DateTimeOffset.Now` startup overhead from ~277ms to ~50ms. Note: `System.TimeZoneInfo.LocalDateTimeOffset` is fastest on API-26+ (Android 8.0+) targets, as it uses [`java.time.ZoneOffset.getTotalSeconds()`][0]. Note: `$(AndroidJavaRuntimeApiLevel)` is updated to API-26 so that `ZoneOffset.getTotalSeconds()` can be used. Care should be taken within `src/java-runtime` to ensure that Android API use is appropriately protected behind runtime version checks. [0]: https://developer.android.com/reference/kotlin/java/time/ZoneOffset#gettotalseconds
* main: Localized file check-in by OneLocBuild Task: Build definition ID 11410: Build ID 6746307 (dotnet#7414) [Mono.Android] Add [Flags] to Android.Nfc.NfcReaderFlags. (dotnet#7410) [monodroid, java-runtime] Add local date time offset monovm prop (dotnet#7331)
Follows up dotnet/runtime#74459 for dotnet/runtime#71004
On dotnet/runtime,
DateTimeOffset.Now
is now updated to leverage an App Context's property as a fast path result if provided. In order to improve startup performance for applications that rely on DateTimeOffset.Now during startup will need to setSystem.TimeZoneInfo.LocalDateTimeOffset
as the appropriate value.The original issue reported a perf timing of 277ms, and after dotnet/runtime#74459, has an average perf timing of 21.818ms.
Testing
Created an Android app
Added
_ = DateTimeOffset.Now;
to OnCreateThe timing of DateTimeOffset.Now with dotnet-trace was 1.18ms + 17.67ms = 18.85ms
This PR does the following:
AndroidJavaRuntimeApiLevel
to 26 to allow forjava.time.OffsetDateTime
andjava.time.ZoneOffset
.