IStorageFolder.GetFilesAsync 之后读取多个 IStorageFile 的性能问题

tmp00000 UID.995403
2017-08-26 发表

本帖最后由 tmp00000 于 2017-8-26 18:56 编辑

我在机械硬盘上创建了 1664 个 png 格式的图片,每一个图片 21.9 KB (22,496 字节)。 使用 UWP 的 FolderPicker 打开了那个存放了图片的文件夹, 使用 IStorageFolder.GetFilesAsync 打开每一个 png 文件, 读取全部内容到一个 Byte 数组,然后关掉它们。 筛选拓展名 png 分别用了 Linq 和 FileQuery 两种方式。这两种方式都需要 接近 6 秒来完成整个过程。 这个速度非常慢。 用 System.IO 的 API 实现等效的过程只需要不到 300 毫秒。 用 C++ 的 FindNextFile 与 用 System.IO 的性能非常接近。但是它在 UWP 上和 System.IO 一样只能对应用沙盒内的文件访问。 有什么好点子在 UWP 下优化效率? ***图片停止解析***

敬告:
为防止不可控的内容风险,本站已关闭新用户注册,新贴的发表及评论;
你现在看到的内容只是互联网用户曾经发表的言论快照,仅用于老用户留存纪念,且仅与科技行业相关,全部内容不代表本站观点及立场;
本站重新开放前已针对包括用户隐私、版权保护、信息安全、国家政策在内的各种互联网法律法规要求,执行了隐患内容的自查、屏蔽和删除;
本站目前所属个人主体,未有任何盈利安排与计划,且与原WFUN.COM所属公司不存在任何关联关系;
如果本帖内容或者相关资源侵犯到您的合法权益,或者您认为存在问题,那么请您务必点此举报或投诉!
全部回复:
TonyDeng UID.2870126
2017-08-26 使用 Lumia 830 回复

底层都不一样,很难比较的。知道它为什么慢才谈得上如何改,需要做专门测试才行。

Mou****uto UID.2885160
2017-08-26 回复

本帖最后由 MouriNaruto 于 2017-8-26 15:29 编辑

如果能提供测试用的图片包,我想试试看

PS:如果为了效率的话,建议学习微软使用C++开发UWP

wcavell UID.34926
2017-08-26 回复

Quote***链接停止解析***
如果能提供测试用的图片包,我想试试看

PS:如果为了效率的话,建议学习微软使用C++开发UWP ...


跟C++没有关系,是微软的api的问题。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 15:15
如果能提供测试用的图片包,我想试试看

PS:如果为了效率的话,建议学习微软使用C++开发UWP ...


C++ 也是用那一套 API。问题是那些 API 速度非常慢。

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 15:09
底层都不一样,很难比较的。知道它为什么慢才谈得上如何改,需要做专门测试才行。 ...


Windows Runtime 不是开源的。我不知道他们为什么把枚举文件和打开文件搞得那么慢。

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
跟C++没有关系,是微软的api的问题。


关于互操作优化(从Managed层转Native是有开销的,尽可能让转换次数越少越好或者干脆写成Native的)
https://docs.microsoft.com/zh-cn/windows/uwp/debug-test-perf/windows-runtime-components-and-optimizing-interop

关于文件访问的优化(我建议楼主看“C# 和 Visual Basic 中的数据流性能”这一段)
https://docs.microsoft.com/zh-cn/windows/uwp/debug-test-perf/optimize-file-access

Mou****uto UID.2885160
2017-08-26 回复

本帖最后由 MouriNaruto 于 2017-8-26 16:42 编辑

Quote***链接停止解析***
Windows Runtime 不是开源的。我不知道他们为什么把枚举文件和打开文件搞得那么慢。 ...


因为StorageFile和StorageFolder需要通过RPC进程间通信让RuntimeBroker帮你打开文件(访问沙盒外的东西需要进程间通信)

而且WinRT提供的不少简单的读取接口都会给你进行多次不必要的拷贝

我建议直接从IInputStream拿raw buffer;不要用FileReader
在C#下把IInputStream转C#数组,我记得有很方便的成员函数

TonyDeng UID.2870126
2017-08-26 使用 Lumia 830 回复

Quotetmp00000 发表于 2017-8-26 16:30
Windows Runtime 不是开源的。我不知道他们为什么把枚举文件和打开文件搞得那么慢。 ...


没开源所以说只能做测试了。根据你的描述,总数据量只有36M不到40M,但效率低,最大的可能是文件打开次数太多,估计与网络连接反复通断效率低的原因类似。要验证这个猜测,可以单独读一个40M的文件试试。在传统的C低级I/O读取操作上,block读和逐个byte读,差异很显著,即使是前者,调整块尺寸也有效果,甚至这与系统的缓存策略有关。这些因素都是猜测,到底真正的原因是什么,要测试验证。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 16:36
因为StorageFile和StorageFolder需要通过RPC进程间通信让RuntimeBroker帮你打开文件(访问沙盒外的东西需 ...


那篇文章只是告诉我怎样用索引优化。只遍历文件名不打开文件在 1 楼 的测试中都需要 1200 毫秒。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 16:36
因为StorageFile和StorageFolder需要通过RPC进程间通信让RuntimeBroker帮你打开文件(访问沙盒外的东西需 ...


看来速度慢是因为有 RuntimeBroker。非得速度快的话我得写一个 Win32 程序来提高效率。 不过 Stream 这种类型在 AppService 里是不能传递的。Win32 程序打开了文件却不能交给 UWP 程序使用。

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
那篇文章只是告诉我怎样用索引优化。只遍历文件名不打开文件在 1 楼 的测试中都需要 1200 毫秒。 ...


***附件停止解析***
你那篇网页没仔细看啊……不仅仅是索引优化啊……

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 16:49
没开源所以说只能做测试了。根据你的描述,总数据量只有36M不到40M,但效率低,最大的可能是文件打开次数 ...


c 的文件 API 非常难用。我之前有个 c 语言文件处理程序没写缓存机制,结果翻译成 .NET 上的程序运行效率提高 15 倍。

Mou****uto UID.2885160
2017-08-26 回复

本帖最后由 MouriNaruto 于 2017-8-26 16:59 编辑

Quote***链接停止解析***
没开源所以说只能做测试了。根据你的描述,总数据量只有36M不到40M,但效率低,最大的可能是文件打开次数 ...


恰好我最近在开发和Socket有关的工具(一个用C++开发的UWP)

1.加大缓冲区,让ReadAsync方法读入尽可能多的内容以减少调用该方法的次数以减小开销和提升效率
2.直接使用IBufferByteAccess接口获取IBuffer的原始指针以减少内存复制

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 16:54
你那篇网页没仔细看啊……不仅仅是索引优化啊……


我现在要做的是打开用户选取的一个目录下的所有 png 文件为 Stream。那篇文章没告诉我怎样真正快速打开沙盒外面用户自己选的文件夹里面特定类型的文件。文件查询那个写法我确实是按照那篇文章做了。但是效率基本上没有提升,有时反而效率变差了。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 16:58
恰好我最近在开发和Socket有关的工具(一个用C++开发的UWP)

1.加大缓冲区,让ReadAsync方法读入尽可能 ...


我要处理的是大量的小文件。每个文件小于 80KB,但是数量会超过 100000。

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
我现在要做的是打开用户选取的一个目录下的所有 png 文件为 Stream。那篇文章没告诉我怎样真正快速打开沙 ...


你的缓冲区设置多大……如果照那篇文章用8字节……那肯定不能看

至少也要8192字节啊……像传输工具的话,至少1MB大小的缓冲区体验才好些

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 16:58
恰好我最近在开发和Socket有关的工具(一个用C++开发的UWP)

1.加大缓冲区,让ReadAsync方法读入尽可能 ...


我要做的程序的意图非常简单。比较两个文件夹内拥有相同文件名的图片。如果尺寸和像素格式相同,则按照一定的容差比较。比较的结果是标出图片的不同(产生尺寸一样的位图),并且标注哪些文件一样,哪些不一样(存为 Json或csv)。

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
我要处理的是大量的小文件。每个文件小于 80KB,但是数量会超过 100000。

建议把缓冲区设为80KB(表示对于现在的设备来说80KB并不奢侈)

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 17:02
你的缓冲区设置多大……如果照那篇文章用8字节……那肯定不能看

至少也要8192字节啊……像传输工具的话 ...


缓冲区大小是 4kb。

Mou****uto UID.2885160
2017-08-26 回复

本帖最后由 MouriNaruto 于 2017-8-26 17:11 编辑

Quote***链接停止解析***
缓冲区大小是 4kb。


4KB,这么小

4KB要读一个最大80KB的文件,最坏情况下需要调用ReadAsync 20次
你要读取100,000以上的文件,最坏要调用该函数2,000,000次
按照ms把大于50ms的操作做成异步,可能需要100,000秒执行时间

补充,如果我没看错你的要求的话,估计你还要注意WinRT接口互操作开销
(“如果在应用的关键路径中多次(几十万到几百万)调用 UWP API,则此成本可能会变得很显著。”摘自微软文档)

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 17:04
建议把缓冲区设为80KB(表示对于现在的设备来说80KB并不奢侈)


在 .NET Framework 上, 读取全部测试用的 1664 个文件只要大概 260 毫秒。代码也很简单,不需要用那些非托管的东西。 但是 UWP 上读取全部文件 7 秒钟都过去了。性能实在是不能接受。 我真正要每天处理的是 10 万 对小型的图片文件。如果光是读取内容就要不可忍受的时间,我的工作效率会非常低。

TonyDeng UID.2870126
2017-08-26 使用 Lumia 830 回复

Quotetmp00000 发表于 2017-8-26 16:57
c 的文件 API 非常难用。我之前有个 c 语言文件处理程序没写缓存机制,结果翻译成 .NET 上的程序运行效率 ...


C有自己的内存管理库,可以不用系统的,而且它最有效率的读写方式就是块读写,之所以很多底层的数据都涉及“对齐”,就是这个原因。在Windows上,磁盘块的尺寸一般是64K,而你的文件却只有22K,还被迫分开1664次打开和关闭,相当于碎片。用系统API快,是系统缓存命中率高了,提前把数据加载到内存了。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 17:07
4KB,这么小

4KB要读一个最大80KB的文件,最坏情况下需要调用ReadAsync 20次


调用的次数是一次。缓冲区大小通过 System.IO.FileStream 的构造函数设置 https://msdn.microsoft.com/en-us/library/d0y914c5(v=vs.110).aspx

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 17:08
C有自己的内存管理库,可以不用系统的,而且它最有效率的读写方式就是块读写,之所以很多底层的数据都涉 ...


通过 FileStream 我会把每个图片分别读取到与图片文件流大小一样的 Byte 数组里面。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 15:15
如果能提供测试用的图片包,我想试试看

PS:如果为了效率的话,建议学习微软使用C++开发UWP ...


使用 C++ 开发这个程序对我而言是个负担。我有自己的本职工作,因此做这个程序需要用非常短的时间。运行效率不必到极致但是不要慢太多。

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 16:33
关于互操作优化(从Managed层转Native是有开销的,尽可能让转换次数越少越好或者干脆写成Native的)
http ...


不像是互操作优化问题。 枚举文件信息 21% 的 CPU, 打开文件 19% 的 CPU, 获取文件名 18.6% 的 CPU。边界花费的 CPU 时间大概 2%。
***图片停止解析***

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
调用的次数是一次。缓冲区大小通过 System.IO.FileStream 的构造函数设置 https://msdn.microsoft.com/en ...


由于我不知道你的实现,所以没法给你准确的建议

如果你使用AsStreamForRead方法获取System.IO.Stream对象,那么你需要AsStreamForRead(缓冲区大小);就像这样

var managedStream = fuckStream.AsStreamForRead(80*1024);

PS:设fuckStream是IInputStream对象

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 17:29
由于我不知道你的实现,所以没法给你准确的建议

如果你使用AsStreamForRead方法获取System.IO.Stream对 ...


用的是一个拓展方法。 System.IO.WindowsRuntimeStorageExtensions.OpenStreamForReadAsync(IStorageFile)

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 17:29
由于我不知道你的实现,所以没法给你准确的建议

如果你使用AsStreamForRead方法获取System.IO.Stream对 ...


照你的代码改了。速度变慢了。原本 5400 毫秒,改了之后 6100 毫秒。

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
照你的代码改了。速度变慢了。原本 5400 毫秒,改了之后 6100 毫秒。


嗯,抱歉,所以我说“没法准确给你建议”

其实我对你使用的图片很感兴趣,想自己写一个读取实现看看怎么写效率会高些
如果我自己觉得效率不错的话,会把代码贴出来

tmp00000 UID.995403
2017-08-26 回复

QuoteMouriNaruto 发表于 2017-8-26 17:38
嗯,抱歉,所以我说“没法准确给你建议”

其实我对你使用的图片很感兴趣,想自己写一个读取实现看看怎么 ...


没事。咱们这些搞 UWP 开发的被 API 坑是常事。 我估计那个 StorageFile 类没有实现延迟加载。我想要打开文件,实际那个类顺手把文件属性也读取了。所以速度变慢了。 不用这种方式遍历目录会拒绝访问。说明文件权限检查也是与 Win32 程序区别对待的。 至于边界优化,我用 .NET Native 编译并且看了反汇编。没看出来哪里能优化。

Mou****uto UID.2885160
2017-08-26 回复

Quote***链接停止解析***
没事。咱们这些搞 UWP 开发的被 API 坑是常事。
我估计那个 StorageFile 类没有实现延迟加载。我想要打开 ...


速度慢也许RPC通信导致的开销也会分一杯羹(表示准确原因难猜)

Quote我估计那个 StorageFile 类没有实现延迟加载。我想要打开文件,实际那个类顺手把文件属性也读取了。所以速度变慢了。

这个倒不会,如果你没有获取文件属性信息的时候,它并不会自作主张给你获取(因为WinRT下的属性其实是COM的get和put方法,如果你没有获取或者修改属性,对应的方法不会执行)

Quote至于边界优化,我用 .NET Native 编译并且看了反汇编。没看出来哪里能优化。

毕竟你立马把WinRT对象转换成.Net对象了,于是边界开销会大大减小

TonyDeng UID.2870126
2017-08-26 使用 Lumia 950 回复

读文件不行的话,可以考虑换一种方案的。

tmp00000 UID.995403
2017-08-26 使用 Lumia 950 XL 回复

QuoteTonyDeng 发表于 2017-8-26 18:44
读文件不行的话,可以考虑换一种方案的。


没别的方案。输入就是一个放着png文件的文件夹。

vbfool UID.352791
2017-08-26 回复

我也试了一下,3000个100k大小的txt文件,CreateFileQueryWithOptions去遍历一遍,就需要5秒钟,如果是打开所有文件的话,需要15-20秒。
看来这个开销问题,还确实挺严重。

TonyDeng UID.2870126
2017-08-26 使用 Lumia 950 回复

Quotetmp00000 发表于 2017-8-26 18:57
没别的方案。输入就是一个放着png文件的文件夹。


有的:当你在那个文件夹中存入文件时,就把该图片的信息写到一个数据库清单上。

tmp00000 UID.995403
2017-08-26 回复

Quotevbfool 发表于 2017-8-26 21:00
我也试了一下,3000个100k大小的txt文件,CreateFileQueryWithOptions去遍历一遍,就需要5秒钟,如果是打开 ...


所以说我怀疑那几个 API 实现的逻辑有问题。可能遍历文件的时候把文件的完整路径和各种乱七八糟的属性都加载了。或者是遍历文件的 RPC 调用没有对遍历文件多的目录优化,打开文件也没对这种情况优化。

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 21:03
有的:当你在那个文件夹中存入文件时,就把该图片的信息写到一个数据库清单上。 ...


图片是从网络下载的。而且数据库放变长数据影响性能。

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 21:03
有的:当你在那个文件夹中存入文件时,就把该图片的信息写到一个数据库清单上。 ...


就算存储了哈希值之类的信息,也不能避免对文件众多的目录的遍历。两三个文件就差不多一毫秒,文件一多,比图程序再怎么优化都被文件遍历和读取拖慢了。

TonyDeng UID.2870126
2017-08-26 使用 Lumia 950 回复

Quotetmp00000 发表于 2017-8-26 21:14
就算存储了哈希值之类的信息,也不能避免对文件众多的目录的遍历。两三个文件就差不多一毫秒,文件一多, ...


我说的数据库是泛指,其实就是一个文本文件,一行储存一张图片的属性数据,如尺寸、分辨率、色深等,以及你比较时可能会用到的数据。对比是读这张表从数据中查找差异,找到后每行记录的文件名就是图片。不要把整张图读进来!

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 21:25
我说的数据库是泛指,其实就是一个文本文件,一行储存一张图片的属性数据,如尺寸、分辨率、色深等,以及 ...


现在问题又回到原点了。我只能得到图片。这种文本文件没办法得到。

TonyDeng UID.2870126
2017-08-26 使用 Lumia 950 回复

Quotetmp00000 发表于 2017-8-26 21:41
现在问题又回到原点了。我只能得到图片。这种文本文件没办法得到。


用C++写一个过渡呗,它生成数据,你去读。

TonyDeng UID.2870126
2017-08-26 使用 Lumia 950 回复

话说没有你数据,不好研究。

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 21:44
用C++写一个过渡呗,它生成数据,你去读。


我的意思是纯 UWP 实现。用 Win32 那一套我有现成的代码,而且不用 C++ 性能也在接受范围内。

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 21:49
话说没有你数据,不好研究。


现成的代码分三个部分。VB 做 WPF 的用户界面以及编写主要的图片文件比较逻辑, C# 是别人写的 OpenCL 包装库, OpenCL 写图片比较核心用。 我现在的打算是把这个程序做成原生 UWP 的。现有的 .NET Framework 实现的代码性能已经很好了。但是我想让它上商店并且兼容手机。

tmp00000 UID.995403
2017-08-26 回复

QuoteTonyDeng 发表于 2017-8-26 21:44
用C++写一个过渡呗,它生成数据,你去读。


这个项目最终的代码不会包含 C++。我只在研究性能的时候使用 C++, 这样可以调查到底是我的代码没写好导致速度慢还是 API 慢。 用 C++ 维护成本太高了,会影响我的本职工作。

vbfool UID.352791
2017-08-27 回复

Quote***链接停止解析***
现成的代码分三个部分。VB 做 WPF 的用户界面以及编写主要的图片文件比较逻辑, C# 是别人写的 OpenCL 包 ...


兼容手机的话,OpenCL那里不会是问题么?
如果没有兼容手机这要求,估计WPF确实是够了。

vbfool UID.352791
2017-08-27 回复

还有就是,没去试并行的执行Query有没有用,是不是会报错,还是可以正常的执行,都不知道,Count的速度倒是快的很。

TonyDeng UID.2870126
2017-08-27 使用 Lumia 950 回复

Quotevbfool 发表于 2017-8-27 21:07
兼容手机的话,OpenCL那里不会是问题么?
如果没有兼容手机这要求,估计WPF确实是够了。 ...


同意(字数补丁)

tmp00000 UID.995403
2017-08-28 回复

Quotevbfool 发表于 2017-8-27 21:07
兼容手机的话,OpenCL那里不会是问题么?
如果没有兼容手机这要求,估计WPF确实是够了。 ...


我最终想把它搞成分布式的。所以要广泛支持多种设备和操作系统。

tmp00000 UID.995403
2017-08-28 回复

Quotevbfool 发表于 2017-8-27 21:07
兼容手机的话,OpenCL那里不会是问题么?
如果没有兼容手机这要求,估计WPF确实是够了。 ...


我现在打算电脑上继续用 WPF 的。手机我再想想办法。

artfly08 UID.2900999
2018-06-26 使用 Lumia 950 XL 回复

支持开发者

artfly08 UID.2900999
2018-07-14 使用 Lumia 950 XL 回复

太专业,支持

本站使用Golang构建,点击此处申请开源鄂ICP备18029942号-4联系站长投诉/举报