【问题标题】:Performance: Android JCIFS file reading is slow性能:Android JCIFS 文件读取速度慢
【发布时间】:2017-08-23 21:45:55
【问题描述】:

首先,我看到了一个现有问题 (JCIFS: file retrieval is too slow to be usable),但它是针对 Java 而不是 Android,建议的答案都不起作用。

我在 Android Studio 2.3 中为 Android SDK 25 (7.1.1) 创建了一个默认项目,将库与 compile 'jcifs:jcifs:1.3.17' 链接起来,并输入了以下简单的测试代码。结果在代码下方。

protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    jcifs.Config.setProperty("jcifs.util.loglevel", "3");
    //jcifs.Config.setProperty("jcifs.smb.client.dfs.disabled", "false");
    //jcifs.Config.setProperty("jcifs.resolveOrder", "DNS");

    try
    {
        NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", ID, PASSWORD);
        final SmbFile smb = new SmbFile("smb://192.168.XX.XX/Share/FileName", auth);

        Thread t = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                Log.d(TAG, "Test Start");

                for(int i = 1000; i<10000; i+=1000)
                    measure(i);

                Log.d(TAG, "Test End");
            }

            private void measure(int bufferSize)
            {
                Log.d(TAG, "=====Buffer: " + bufferSize + "============");
                try
                {
                    byte[] buffer = new byte[bufferSize];
                    int read = 0;
                    InputStream str = smb.getInputStream();
                    long start = System.nanoTime();

                    while(read < 1000000)
                        read += str.read(buffer);

                    long end = System.nanoTime();
                    str.close();
                    float time = (float) ((end - start) / 1000000000d);
                    float speed = (float) read / 1048576 / time;
                    Log.d(TAG, "Time:" + time + ", size =" + read);
                    Log.d(TAG, "Speed =  " + speed + "MB/s");
                }
                catch(IOException exc)
                {
                    exc.printStackTrace();
                }
            }
        });
        t.start();
    }
    catch(Exception exc)
    {
        Log.d(TAG, exc.toString());
    }
}

结果

Test Start
=====Buffer: 1000============
Time:2.210785, size =1000000
Speed =  0.43137363MB/s
=====Buffer: 2000============
Time:1.4158936, size =1000000
Speed =  0.6735495MB/s
=====Buffer: 3000============
Time:1.0556641, size =1002000
Speed =  0.9051948MB/s
=====Buffer: 4000============
Time:0.7543335, size =1000000
Speed =  1.2642609MB/s
=====Buffer: 5000============
Time:3.6557617, size =1000000
Speed =  0.26086885MB/s
=====Buffer: 6000============
Time:3.292389, size =1002000
Speed =  0.2902396MB/s
=====Buffer: 7000============
Time:2.9179688, size =1001000
Speed =  0.32715496MB/s
=====Buffer: 8000============
Time:2.462616, size =1000000
Speed =  0.38726068MB/s
=====Buffer: 9000============
Time:3.9379272, size =1008000
Speed =  0.24411413MB/s
Test End

读取速度约为 0.2MB/s ~ 1.2MB/s。设备连接150Mbps Wi-Fi,理论上可以达到10MB/s以上。 SMB 服务器也不慢。当我将文件复制到笔记本电脑时,读取速度约为 30MB/s。

为什么这么慢?我应该检查什么?如果缓冲区大小为 4000,为什么读取速度大约高 5 倍(1.2MB/s)?

顺便说一句,我已经测试过使用其他商业应用程序复制相同的文件。 File Commander、Asus File Manager 显示同样低速,ES File Explorer 显示约 2MB/s,Solid Explorer 显示约 5MB/s。因为我很确定他们都使用 JCIFS(尽管它的版本可能略有不同),所以必须有一种方法可以像 Solid Explorer 那样实现至少 5MB/s。

【问题讨论】:

    标签: android performance optimization jcifs


    【解决方案1】:

    在Windows电脑上使用WireShark(网络分析工具)后发现,无论我设置哪个缓冲区大小,read SMB命令总是给Windows电脑4286字节。 SmbFileInputStream.java 似乎正在使用服务器的最大缓冲区大小。

    但是当我看到来自 Solid Explorer 的数据包时,它是 32768 字节。所以,我反编译了 Solid Explorer 的 APK(它当然被混淆了),并在其中看到了 SmbFileInputStream.java 文件(该文件属于 JCIFS)。似乎 Solid Explorer 的开发人员修改了该文件,并设置了更大的readSize。所以,我尝试了类似的事情。然后我在上面的相同代码中实现了 5MB/s。

    由于 JCIFS 附带 LGPL,Solid Explorer 使用修改后的 JCIFS 而不公开源代码的事实违反了 JCIFS 的许可。但是,哦,好吧,似乎很多 Android 应用程序开发人员都忽略了他们使用的库的许可证。他们甚至没有正确地归功于他们使用的开源库。

    【讨论】:

    • 也许他们使用了我回答中提到的补丁?
    【解决方案2】:

    您是否尝试过:jcifs.Config.setProperty("jcifs.smb.client.dfs.disabled", "true");

    在我的情况下(虽然是 Java),它有助于慢速连接。默认为false

    对我来说唯一的问题是我不确定该属性是否会“破坏”其他工作正常的东西..

    【讨论】:

    • 是的,如果您仔细观察,我已经在我的原始代码示例中尝试过。正如我所写的那样,问题在于消息较小,这会带来更多的往返次数和开销。
    【解决方案3】:

    有一个大缓冲区读取的补丁:

    从自述文件内部:

    这个补丁增加了两个 SMB,据说可以改善读写 性能相当。不幸的是,目前还不清楚 所有实现都正确支持命令。请注意,在 除了这个补丁,还需要添加一个“& 0xFFFF” SmbTransport.java:doRecv:~437 显示为:

    int size = Encdec.dec_uint16be(BUF, 2) & 0xFFFF;

    尽管在 1.2.7 中已进行了此更改。

    不确定这是否适用于 Android,但解决方案可能类似。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      • 2014-04-18
      • 2022-01-24
      • 2013-01-13
      • 2018-03-12
      • 1970-01-01
      相关资源
      最近更新 更多