主要操作:(文件去重)
function files_do(){
let MD5 = 读(buffer 生成 MD5);
if(从数据库找相同 MD5 的值.Count()==0){
//没找到
数据库.insert(文件信息);
}else{
//有相同值
文件移动(原路径, 目标路径);
}
setTimeout(function(){
files_do()
},0)
}
目前 5 万个文件(大小: 几 k 到十几 M 都有), 大概需要 6 个小时多..
需要处理几百万个文件.
有什么办法能进一步提高执行效率??
1
TJT 2017-04-21 12:24:49 +08:00 1
先分析性能消耗在哪里。可能的优化方法有数据库所有的 MD5 放到 Set 里面,这样就直接干掉数据库了、多线程 /多进程、用 C 重写。
|
2
crayygy 2017-04-21 12:34:54 +08:00 via iPhone 1
粗略的想了一下,主要的消耗有两个,一是读文件, IO 消耗比较大,二是数据库查询,消耗也不小。第一个没办法,如果你对内容要求不多,直接对整个文件做校验或许就可以了。数据库的话就像楼上说的,几万个也不多,直接存在内存里,然后统一写数据库。
|
3
XiaoxiaoPu 2017-04-21 13:14:05 +08:00 1
不要直接比较 md5 ,先比较文件长度,再比较文件的部分内容(比如开头 1 KiB ),最后比较 md5 。
|
4
keller 2017-04-21 13:18:50 +08:00 1
不要直接对比 md5 先对比文件大小 相同大小的文件再做 md5 对比
|
5
actto OP 谢谢大家!!!
1, 我目前就用的内存数据库, 定期写回 db 文件里..所以目前数据库查询效率还可以. 2, "先比较文件大小" 不可行..因为 db.insert(文件信息) 必须有 MD5 值(因为后面的文件需要和它比对)....也还是得读一遍文件 buffer... 3, 现在考虑用 C++写 多线程读 buffer,并生成 MD5 的操作..先写入数据库..全部结束后, 数据库查重复, 再移动文件. |
6
lldld 2017-04-21 18:40:40 +08:00 via iPhone
@actto 楼上
@XiaoxiaoPu 的建议不错。 读文件的时候不要读整个文件求 md5. 可以分三步: 1) 按照文件大小和文件前 256bytes 的 md5 来确定可能会重复的文件 2) 对 1 的结果求整个文件的 md5, 确定重复的文件 3) 去重 |
7
coderfox 2017-04-21 22:57:49 +08:00 via Android
读 buffer 生成 MD5 可以换成调用 md5 终端指令。也许能改善,取决于 node 的 md5 实现如何。
|
8
breeswish 2017-04-22 00:07:53 +08:00
md5 没啥性能瓶颈的,一般单核每秒都有几百兆。楼主需要先看一下 node CPU 是否跑满单核了,按照楼主 po 的代码来看是有异步性能浪费的( setTimeout ),当然实际代码也许不是长成这样。如果单核已经跑满了,可以考虑上多个进程并行运行。
优化的上线应当接近于这 5 万个文件全部读取一遍的性能。 |
9
breeswish 2017-04-22 00:16:50 +08:00
另外楼主可以考虑直接将文件流 pipe 到 hash ,可以避免频繁的 buffer alloc ,进一步压榨一些性能。
用 C++ 生成 md5 就不用想了, nodejs 人家不是用 js 算的 md5 ,是 openssl 计算的,自己写的效率一般不会更高。 |
10
breeswish 2017-04-22 00:23:12 +08:00
当然上面说的那些的前提是不频繁访问数据库。数据库一般是个瓶颈,一楼已经给出优化数据库的方法了。
不过不建议用 C 重写 :P 这个需求的终极瓶颈理论上在 IO ,如果测下来瓶颈不在 IO 上那么就是可以优化的。以及既然终极瓶颈是 IO ,那么我猜测不用异步写 C 的话会写得单核性能比 nodejs 版还要低。 |
11
actto OP @breeswish 谢谢给出的建议!!!
我的代码确实有用到 setTimeout ,因为希望页面上能反应出进度,给 dom 操作留点时间。(electron 小程序,数据库用的 LokiJS) 整体代码就是递归循环整个文件夹,找到所有的文件去重。重复文件放在固定文件夹,等待人工检查。 cpu 目前占用 20%不到(18%— 19%左右)。 |
14
breeswish 2017-04-22 12:19:34 +08:00
@actto 这么来说目测你的 IO 用的也都是同步的 IO 了……?那么你首先要做的是全改成异步的 IO ,然后用上一些异步流程控制的库比如 caolan/async 来进行控制(比如可以用上它的 queue )。然后 setTimeout 可以去掉了
|
15
qfdk 2017-04-22 14:19:20 +08:00 via iPhone
你把文件合并一下 大体看看读取 500m 是个啥速度 按道理 应该不慢 另外读取文件有异步和同步两种方式
|