Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Commit 9876607

Browse files
jonathanpepperssamhouts
authored andcommitted
[android/ios] improve perf when not using Application.Properties (#8887)
* [android/ios] improve perf when not using Application.Properties When profiling startup, I was seeing things like: Method call summary Total(ms) Self(ms) Calls Method name 52 0 3 System.IO.IsolatedStorage.IsolatedStorageFile:GetUserStoreForApplication () 52 0 3 (wrapper remoting-invoke-with-check) System.IO.IsolatedStorage.IsolatedStorageFile:PostInit () 51 0 3 System.IO.IsolatedStorage.IsolatedStorageFile:PostInit () 51 0 2 (wrapper remoting-invoke-with-check) System.IO.IsolatedStorage.IsolatedStorageFile:FileExists (string) 51 0 2 System.IO.IsolatedStorage.IsolatedStorageFile:FileExists (string) 49 0 4 System.IO.IsolatedStorage.IsolatedStorageFile:IsPathInStorage (string) This app has no `Properties` at all, but it seems to still be doing the file I/O for it. I found that a file was being written that contain a serialized empty dictionary: > adb shell run-as com.xamarin.forms.helloforms cat /data/user/0/com.xamarin.forms.helloforms/files/.config/.isolated-storage/PropertyStore.forms @▲ArrayOfKeyValueOfstringanyTyp9http://schemas.microsoft.com/2003/10/Serialization/Arrays ☺i)http://www.w3.org/2001/XMLSchema-instance☺ Any subsequent startup would parse this file. Two changes will help here: 1) For writes, use a single instance of: using (var store = IsolatedStorageFile.GetUserStoreForApplication()) It appears this call is expensive. 2) On writes, we don't need to write a file at all if the `Properties` dictionary is empty and no file is on disk. This way apps that don't use `Properties` at all won't load a file with an empty dictionary. ~~ Results ~~ Using a Release build of the Blank Forms app template on a Pixel 3 XL: Before: 12-13 14:01:42.826 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +606ms 12-13 14:01:46.541 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +588ms 12-13 14:01:50.325 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +597ms 12-13 14:01:54.088 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +600ms 12-13 14:01:57.855 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +579ms 12-13 14:02:01.619 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +594ms 12-13 14:02:05.371 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +605ms 12-13 14:02:09.106 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +597ms 12-13 14:02:12.869 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +600ms 12-13 14:02:16.635 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +601ms Average(ms): 596.7 Std Err(ms): 2.56493448042808 Std Dev(ms): 8.11103500725332 After: 12-13 13:59:46.260 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +600ms 12-13 13:59:49.993 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +579ms 12-13 13:59:53.739 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +587ms 12-13 13:59:57.506 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +592ms 12-13 14:00:01.255 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms 12-13 14:00:05.007 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +580ms 12-13 14:00:08.841 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms 12-13 14:00:12.587 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +591ms 12-13 14:00:16.320 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +585ms 12-13 14:00:20.052 1490 1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +577ms Average(ms): 585.8 Std Err(ms): 2.23507394856536 Std Dev(ms): 7.06792441637257 I think this change saves ~10ms on startup for apps that don't use `Application.Properties` at all. I made this change for Android & iOS, as they had similar implementations. * Moved the properties check earlier This way it won't even open the Stream on writes when there are no properties. * [tizen] include the same optimization As suggested by @rookiejava: #8887 (review) * [tizen] make implementation match Android exactly Context: #8887 (comment) As suggested by @rookiejava, we can make the implementation match other platforms exactly. I took the changes as-is, except matched the formatting in Android's `Deserializer.cs` so the two files are identical except for the namespace. I also had to use `Internals.Log`.
1 parent 5f4f4dd commit 9876607

File tree

3 files changed

+86
-95
lines changed

3 files changed

+86
-95
lines changed

Xamarin.Forms.Platform.Android/Deserializer.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,30 @@ public Task SerializePropertiesAsync(IDictionary<string, object> properties)
5555
// Make sure to use Internal
5656
return Task.Run(() =>
5757
{
58-
var success = false;
5958
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
60-
using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
61-
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
6259
{
63-
try
60+
// No need to write 0 properties if no file exists
61+
if (properties.Count == 0 && !store.FileExists(PropertyStoreFile))
6462
{
65-
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
66-
dcs.WriteObject(writer, properties);
67-
writer.Flush();
68-
success = true;
63+
return;
6964
}
70-
catch (Exception e)
65+
using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
66+
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
7167
{
72-
Debug.WriteLine("Could not serialize properties: " + e.Message);
73-
Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
68+
try
69+
{
70+
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
71+
dcs.WriteObject(writer, properties);
72+
writer.Flush();
73+
}
74+
catch (Exception e)
75+
{
76+
Debug.WriteLine("Could not serialize properties: " + e.Message);
77+
Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
78+
return;
79+
}
7480
}
75-
}
7681

77-
if (!success)
78-
return;
79-
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
80-
{
8182
try
8283
{
8384
if (store.FileExists(PropertyStoreFile))
Lines changed: 52 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Threading.Tasks;
3+
using System.Diagnostics;
4+
using System.IO.IsolatedStorage;
45
using System.Runtime.Serialization;
6+
using System.Threading.Tasks;
57
using System.Xml;
6-
using System.Diagnostics;
7-
using System.IO;
8-
using Xamarin.Forms.Internals;
98

109
namespace Xamarin.Forms.Platform.Tizen
1110
{
12-
internal class Deserializer : IDeserializer
11+
internal class Deserializer : Internals.IDeserializer
1312
{
1413
const string PropertyStoreFile = "PropertyStore.forms";
1514

@@ -19,33 +18,29 @@ public Task<IDictionary<string, object>> DeserializePropertiesAsync()
1918
// Make sure to use Internal
2019
return Task.Run(() =>
2120
{
22-
var store = new TizenIsolatedStorageFile();
23-
Stream stream = null;
24-
try
21+
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
2522
{
26-
stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.OpenOrCreate);
27-
if (stream.Length == 0)
28-
{
23+
if (!store.FileExists(PropertyStoreFile))
2924
return null;
30-
}
25+
26+
using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
3127
using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
3228
{
33-
stream = null;
34-
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
35-
return (IDictionary<string, object>)dcs.ReadObject(reader);
36-
}
37-
}
38-
catch (Exception e)
39-
{
40-
Debug.WriteLine("Could not deserialize properties: " + e.Message);
41-
Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while reading Application properties: {e}");
42-
}
43-
finally
44-
{
45-
if (stream != null)
46-
{
47-
stream.Dispose();
29+
if (stream.Length == 0)
30+
return null;
31+
32+
try
33+
{
34+
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
35+
return (IDictionary<string, object>)dcs.ReadObject(reader);
36+
}
37+
catch (Exception e)
38+
{
39+
Debug.WriteLine("Could not deserialize properties: " + e.Message);
40+
Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while reading Application properties: {e}");
41+
}
4842
}
43+
4944
}
5045

5146
return null;
@@ -59,49 +54,43 @@ public Task SerializePropertiesAsync(IDictionary<string, object> properties)
5954
// Make sure to use Internal
6055
return Task.Run(() =>
6156
{
62-
var success = false;
63-
var store = new TizenIsolatedStorageFile();
64-
Stream stream = null;
65-
try
57+
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
6658
{
67-
stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate);
68-
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
59+
// No need to write 0 properties if no file exists
60+
if (properties.Count == 0 && !store.FileExists(PropertyStoreFile))
6961
{
70-
stream = null;
71-
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
72-
dcs.WriteObject(writer, properties);
73-
writer.Flush();
74-
success = true;
62+
return;
7563
}
76-
}
77-
catch (Exception e)
78-
{
79-
Debug.WriteLine("Could not serialize properties: " + e.Message);
80-
Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
81-
}
82-
finally
83-
{
84-
if (stream != null)
64+
using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
65+
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
8566
{
86-
stream.Dispose();
67+
try
68+
{
69+
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
70+
dcs.WriteObject(writer, properties);
71+
writer.Flush();
72+
}
73+
catch (Exception e)
74+
{
75+
Debug.WriteLine("Could not serialize properties: " + e.Message);
76+
Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
77+
return;
78+
}
8779
}
88-
}
89-
90-
if (!success)
91-
return;
9280

93-
try
94-
{
95-
if (store.FileExists(PropertyStoreFile))
96-
store.DeleteFile(PropertyStoreFile);
97-
store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
98-
}
99-
catch (Exception e)
100-
{
101-
Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
102-
Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
81+
try
82+
{
83+
if (store.FileExists(PropertyStoreFile))
84+
store.DeleteFile(PropertyStoreFile);
85+
store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
86+
}
87+
catch (Exception e)
88+
{
89+
Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
90+
Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
91+
}
10392
}
10493
});
10594
}
10695
}
107-
}
96+
}

Xamarin.Forms.Platform.iOS/Deserializer.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,30 @@ public Task SerializePropertiesAsync(IDictionary<string, object> properties)
5555
// Make sure to use Internal
5656
return Task.Run(() =>
5757
{
58-
var success = false;
5958
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
60-
using (var stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
61-
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
6259
{
63-
try
60+
// No need to write 0 properties if no file exists
61+
if (properties.Count == 0 && !store.FileExists(PropertyStoreFile))
6462
{
65-
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
66-
dcs.WriteObject(writer, properties);
67-
writer.Flush();
68-
success = true;
63+
return;
6964
}
70-
catch (Exception e)
65+
using (var stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
66+
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
7167
{
72-
Debug.WriteLine("Could not serialize properties: " + e.Message);
73-
Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
68+
try
69+
{
70+
var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
71+
dcs.WriteObject(writer, properties);
72+
writer.Flush();
73+
}
74+
catch (Exception e)
75+
{
76+
Debug.WriteLine("Could not serialize properties: " + e.Message);
77+
Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
78+
return;
79+
}
7480
}
75-
}
7681

77-
if (!success)
78-
return;
79-
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
80-
{
8182
try
8283
{
8384
if (store.FileExists(PropertyStoreFile))

0 commit comments

Comments
 (0)