Xamarin Android - 便携式共享库代码以极慢的速度运行

时间:2016-01-08 20:54:05

标签: android performance xamarin garbage-collection xamarin.android

我正在尝试使用Xamarin创建一个应用程序。我有一个可移植的共享库,其中包含一些需要一段时间才能处理的代码。我已经创建了一个使用此库的控制台应用程序,而且我还在使用与Xamarin.Android相同的便携式共享库的Android应用程序,但共享便携式代码在Android上的运行速度非常慢。 / p>

编辑:我也尝试将我的代码放在共享项目中,而不是共享库中,看看它是否能以正常速度运行,但它没有帮助。

我用秒表来计算一些方法:

原始

return p => p.EndsWith(endingString)

使用秒表

return (p =>
{
    Stopwatch sw = Stopwatch.StartNew();
    bool ans = p.EndsWith(endingString);
    sw.Stop();
    Debug.WriteLine(sw.ElapsedTicks);
    return ans;
});

由于某种原因,Android应用程序真的很慢。上面的代码告诉我,只是为了检查字符串是否以某些东西结束在Android上大约需要1411个刻度,而在我的计算机上它只需要5个刻度,这意味着Android版本在Android上花费了280倍以上的刻度。

另一种方法是在Android上使用了243669个刻度,在我的计算机上使用了4561个刻度,这意味着需要53倍的刻度。

以下方法在Android上使用了19847785个刻度,在我的计算机上使用了819619,速度慢了24倍。我不认为缓慢是由我的代码特别引起的,但我想提供一些代码示例,以防万一我的代码中的某些内容引起。

private void InitialiseFrequencyDictionary()
{
    if (_frequencyDictionary != null) return;
    StreamReader streamReader = EmbeddedResourceGetter.GetFrequencyListStreamReader();
    _frequencyDictionary = new Dictionary<string, string>();
    string line;

    while (!string.IsNullOrEmpty(line = streamReader.ReadLine()))
    {
        try
        {
            string[] lineSplit = line.Split(new[] {' '}, 2);
            _frequencyDictionary.Add(lineSplit[0], lineSplit[1]);
        }
        catch { /* ignored */ }
    }
}

整个班级的公共方法整体上在Android上获得155524065个刻度并且花了15761ms。它在我的计算机上花了2471652个刻度和1152毫秒,这意味着它在Android上花费了63倍,在Android上比在我的计算机上花了15倍。

我知道这些结果并不完全准确,因为我在不同的设备,不同的操作系统和不同的架构上测试它们,但我不觉得这些准确地代表了我的计算机和我的手机。我的手机在Geekbench的得分分别为1219和4273,单核和多核得分,我的电脑在Geekbench得分为3288和6978。这意味着我的手机应该比我的电脑慢3倍,而不是16倍!

每当在我的Android设备上执行此代码时,我在Visual Studio的“输出”部分中从垃圾收集器(我认为)中收到了大量消息。

01-08 18:48:27.527 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 0.90ms, total 1.08ms, bridge 0.00ms promoted 880K major 880K los 8K
01-08 18:48:30.133 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:30.133 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 0.85ms, total 0.98ms, bridge 0.00ms promoted 880K major 880K los 8K
01-08 18:48:32.760 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:32.760 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 1.81ms, total 2.06ms, bridge 0.00ms promoted 896K major 896K los 8K
01-08 18:48:35.034 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:35.034 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 1.19ms, total 1.37ms, bridge 0.00ms promoted 912K major 912K los 8K
01-08 18:48:37.687 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:37.687 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 0.59ms, total 1.61ms, bridge 0.00ms promoted 912K major 912K los 8K
01-08 18:48:39.298 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.298 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 4.78ms, total 5.13ms, bridge 0.00ms promoted 1248K major 1248K los 296K
01-08 18:48:39.378 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.378 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 7.58ms, total 7.81ms, bridge 0.00ms promoted 2336K major 2336K los 1348K
01-08 18:48:39.459 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.459 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 7.24ms, total 7.46ms, bridge 0.00ms promoted 3488K major 3488K los 2821K
01-08 18:48:39.536 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.536 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 6.13ms, total 6.40ms, bridge 0.00ms promoted 4624K major 4624K los 2821K
01-08 18:48:39.627 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.627 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 8.29ms, total 8.51ms, bridge 0.00ms promoted 5776K major 5776K los 5877K
01-08 18:48:39.705 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.705 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 6.47ms, total 6.68ms, bridge 0.00ms promoted 6832K major 6832K los 5877K
01-08 18:48:39.796 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.796 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 15.16ms, total 16.35ms, bridge 0.00ms promoted 7888K major 7888K los 5877K
01-08 18:48:39.876 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 23 num_hash_entries 29 sccs size 29 init 0.00ms df1 0.05ms sort 0.00ms dfs2 0.09ms setup-cb 0.01ms free-data 0.03ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:39.876 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.47ms, total 5.68ms, bridge 0.00ms promoted 8928K major 8928K los 5877K
01-08 18:48:40.031 I/art     ( 9357): Explicit concurrent mark sweep GC freed 6197(237KB) AllocSpace objects, 0(0B) LOS objects, 45% free, 4MB/8MB, paused 1.286ms total 18.093ms
01-08 18:48:40.032 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 7/7/7/1 dfs passes 52/31
01-08 18:48:40.032 D/Mono    ( 9357): GC_MAJOR: (Minor allowance) pause 33.66ms, total 36.59ms, bridge 0.00ms major 9968K/0K los 8789K/0K
01-08 18:48:40.110 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.110 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.65ms, total 5.88ms, bridge 0.00ms promoted 11008K major 11008K los 8789K
01-08 18:48:40.190 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.190 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 6.16ms, total 6.37ms, bridge 0.00ms promoted 12032K major 12032K los 8789K
01-08 18:48:40.283 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.283 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 14.61ms, total 17.42ms, bridge 0.00ms promoted 13104K major 13104K los 8789K
01-08 18:48:40.373 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.373 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.86ms, total 6.18ms, bridge 0.00ms promoted 14144K major 14144K los 8789K
01-08 18:48:40.455 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.455 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.82ms, total 6.05ms, bridge 0.00ms promoted 15168K major 15168K los 8789K
01-08 18:48:40.536 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.06ms sort 0.01ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.536 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.51ms, total 5.72ms, bridge 0.00ms promoted 16224K major 16224K los 8789K
01-08 18:48:40.772 I/art     ( 9357): Explicit concurrent mark sweep GC freed 338(11KB) AllocSpace objects, 0(0B) LOS objects, 45% free, 4MB/8MB, paused 838us total 17.067ms
01-08 18:48:40.772 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 7/7/7/1 dfs passes 52/31
01-08 18:48:40.773 D/Mono    ( 9357): GC_MAJOR: (Minor allowance) pause 74.78ms, total 78.16ms, bridge 0.00ms major 17264K/0K los 18218K/0K
01-08 18:48:40.850 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.850 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.58ms, total 5.81ms, bridge 0.00ms promoted 18304K major 18304K los 18218K
01-08 18:48:40.929 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:40.929 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.98ms, total 6.53ms, bridge 0.00ms promoted 19344K major 19344K los 18218K
01-08 18:48:41.008 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:41.008 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.53ms, total 5.74ms, bridge 0.00ms promoted 20400K major 20400K los 18218K
01-08 18:48:41.089 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:41.089 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 7.68ms, total 7.74ms, bridge 0.00ms promoted 21440K major 21440K los 18230K
01-08 18:48:41.168 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:41.168 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.56ms, total 5.77ms, bridge 0.00ms promoted 22496K major 22496K los 18230K
01-08 18:48:41.247 D/Mono    ( 9357): GC_OLD_BRIDGE num-objects 21 num_hash_entries 24 sccs size 24 init 0.00ms df1 0.05ms sort 0.02ms dfs2 0.06ms setup-cb 0.01ms free-data 0.02ms links 0/0/0/0 dfs passes 0/0
01-08 18:48:41.247 D/Mono    ( 9357): GC_MINOR: (Nursery full) pause 5.64ms, total 6.09ms, bridge 0.00ms promoted 23520K major 23520K los 18230K

所以 Xamarin的这种缓慢正常吗?这是Xamarin中的一个错误,还是有什么我不知道的?这是由垃圾收集器引起的吗?

1 个答案:

答案 0 :(得分:1)

我已经调查了一下。这个问题不仅存在于使用PCL时,而且还存在于Android资产中的单词列表时。

首先我尝试过这样的事情:

public static async Task<Dictionary<string, int>> ReadWordFrequenciesAsync(Stream stream)
{
    var freqs = new Dictionary<string, int>();

    using (var reader = new StreamReader(stream))
    {
        string line;
        while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null)
        {
            var split = line.Split(' ');
            var word = split[0];
            var frequency = 0;

            if (int.TryParse(split[1], out frequency))
                freqs.Add(word, frequency);

            line = null;
        }
    }

    return freqs;
}

public static Stream GetWordListFromPcl()
{
    var assembly = typeof(WordListReader).GetTypeInfo().Assembly;
    return assembly.GetManifestResourceStream("WordList.Content.en.txt");
}

在我的电脑上花了54毫秒,在我的Nexus 5上花了大约8秒钟。

修改代码以不将频率解析为int,如下所示:

public static async Task<Dictionary<string, string>> ReadWordFrequenciesAsync(Stream stream = null)
{
    var freqs = new Dictionary<string, string>();

    using (var reader = new StreamReader(stream))
    {
        string line;
        while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null)
        {
            var split = line.Split(' ');
            var word = split[0];
            var frequency = split[1];
            freqs.Add(word, frequency);

            line = null;
        }
    }

    return freqs;
}

这使得代码在Nexus 5上快了2秒。

我在Android资源中尝试过相同的操作,我在这里阅读了如下流:

var stream = Assets.Open("en.txt");

这导致相同的读取时间。这意味着PCL没有错误。

我尝试的最后一件事是将整个文件读入内存。像这样:

public static async Task<string> ReadWordFrequenciesStringAsync(Stream stream = null)
{
    using (var reader = new StreamReader(stream))
    {
        return await reader.ReadToEndAsync().ConfigureAwait(false);
    }
}

这需要不到一秒钟!

好的,那么结论是什么?我认为line.Split方法是错误的!我们可以做些什么呢?

我们可以尝试实现我们自己的分离器。试过这样的事情:

public static class StringExtensions
{
    public static Tuple<string, string> MySplit(this string s, char c)
    {
        var l = s.Length;
        for (var i = 0; i < l; i++)
        {
            if (s[i] == c)
            {
                return new Tuple<string, string>(s.Substring(0, i), s.Substring(i + 1));
            }
        }

        return null;
    } 
}

这节省了一整秒,同时仍然将频率值解析为int。如果我们使用MySplit作为返回值,那么string[]的效果就相同,因此获得的数据并不多。

使用不安全的代码可能有助于提高性能。但是时间实际上并不是读取文件,而是解析它。