0

我有大约 25000 个文件分散在许多文件夹中,这些文件夹在 2 个外部硬盘驱动器上的大小在 5MB 和 200MB 之间。我需要找出其中哪些是重复的,只在驱动器上留下唯一的文件。

目前我正在md5_file()检查每个源文件并比较它们以查看之前是否找到了相同的文件。这样做的问题是,md5_file()执行起来很容易需要超过 10 秒,我已经看到它甚至需要一分钟才能执行某些文件。如果我让这个脚本以它当前的形式运行,那意味着这个过程需要一个多星期才能完成。

请注意,我会在创建一个哈希后保存每个哈希,因此我不必在每次运行时重新哈希每个文件。问题是所有这些文件还没有被散列。

我想知道我能做些什么来加快这个速度。我需要在不到 5 天的时间内完成这项工作,因此需要一周以上的脚本是没有选择的。我在想多线程(使用pthread)可能是一个解决方案,但由于驱动器很慢而且我的 CPU 不是问题,我认为这不会有帮助。我还能做什么?

4

2 回答 2

2

正如您所猜测的那样,很难判断您是否可以通过使用线程来看到任何收益......

但是,我决定根据您的想法编写一个不错的 pthreads 示例,我认为它很好地说明了您在线程处理时应该做的事情......

你的里程会有所不同,但这里的例子都是一样的:

<?php
/* create a mutex for readable logging output */
define ("LOG", Mutex::create());

/* log a message to stdout, use as thread safe printf */
function out($message, $format = null) {
    $format = func_get_args();

    if ($format) {
        $message = array_shift(
            $format);

        Mutex::lock(LOG);
        echo vsprintf(
            $message, $format
        );
        Mutex::unlock(LOG);
    }
}

/*
 Sums is a collection of sum => file shared among workers
*/
class Sums extends Stackable {
    public function run(){}
}

/* Worker to execute sum tasks */
class CheckWorker extends Worker {
    public function run() {}
}

/* 
 The simplest version of a job that calculates the checksum of a file
*/
class Check extends Stackable {

    /* all properties are public */
    public $file;
    public $sum;

    /* accept a file and Sums collection */
    public function __construct($file, Sums &$sums) {
        $this->file = $file;
        $this->sums = $sums;
    }

    public function run(){
        out(
            "checking: %s\n", $this->file);

        /* calculate checksum */
        $sum = md5_file($this->file);

        /* check for sum in list */
        if (isset($this->sums[$sum])) {

            /* deal with duplicate */
            out(
                "duplicate file found: %s, duplicate of %s\n", $this->file, $this->sums[$sum]);
        } else {
            /* set sum in shared list */
            $this->sums[$sum] = $this->file;

            /* output some info ... */
            out(
                "unique file found: %s, sum (%s)\n", $this->file, $sum);
        }
    }
}

/* start a timer */ 
$start = microtime(true);

/* checksum collection, shared across all threads */
$sums = new Sums();

/* create a suitable amount of worker threads */
$workers = array();
$checks = array();
$worker = 0;

/* how many worker threads you have depends on your hardware */
while (count($workers) < 16) {
    $workers[$worker] = new CheckWorker();
    $workers[$worker]->start();
    $worker++;
}

/* scan path given on command line for files */
foreach (scandir($argv[1]) as $id => $path) {

    /* @TODO(u) write code to recursively scan a path */
    $path = sprintf(
        "%s/%s",
        $argv[1], $path
    );

    /* create a job to calculate the checksum of a file */
    if (!is_dir($path))  {
        $checks[$id] = new Check(
            $path, $sums);

        /* @TODO(u) write code to stack to an appropriate worker */
        $workers[array_rand($workers)]->stack($checks[$id]);
    }
}

/* join threads */
foreach ($workers as $worker) {
    $worker->shutdown();
}

/* output some info */
out("complete in %.3f seconds\n", microtime(true)-$start);

/* destroy logging mutex */
Mutex::destroy(LOG);
?>

尝试一下,看看不同数量的工作人员如何影响运行时间,并实现你自己的逻辑来删除文件和扫描目录(这是你应该已经知道的基本知识,为了一个简单的例子而省略了)......

于 2013-08-23T10:40:25.667 回答
0

您可以尝试仅通过查看文件大小来查找可能的重复项。只有当多个文件具有相同的大小时,您才需要对它们进行哈希处理。这可能更快,因为查找文件大小并不费力。

于 2013-08-16T11:30:37.683 回答