web-dev-qa-db-ja.com

2つのファイルが等しいかどうかをチェックする最速のハッシュアルゴリズムは何ですか?

2つのファイルが等しいかどうかを確認するために使用されるハッシュ関数を作成する最も速い方法は何ですか?

セキュリティはそれほど重要ではありません。

編集:私はネットワーク接続を介してファイルを送信しており、両側のファイルが等しいことを確認します

56
eflles

1つのアプローチは、単純なCRC-32アルゴリズムを使用することで、CRC値が等しい場合のみ、SHA1またはより堅牢なものでハッシュを再実行します。高速CRC-32は、いつでも暗号で保護されたハッシュよりも優れています。

25
Greg Hewgill

本当に複雑なハッシュや低速のハッシュを使用していない限り、ディスクからのデータの読み込みは、ハッシュの計算よりもはるかに時間がかかります(RAMディスクまたはトップエンドSSDを使用しない限り)。

したがって、2つのファイルを比較するには、このアルゴリズムを使用します。

  • サイズを比較する
  • 日付を比較します(ここで注意してください:これは間違った答えを与える可能性があります。これがあなたに当てはまるかどうかをテストする必要があります)
  • ハッシュを比較する

これにより、高速フェイルが可能になります(サイズが異なる場合、ファイルが異なることがわかります)。

処理をさらに高速化するために、ハッシュを一度計算して、ファイルとともに保存できます。また、ファイルの日付とサイズをこの追加ファイルに保存します。これにより、メインファイルが変更されたときにハッシュを再計算するか、ハッシュファイルを削除する必要があるかがすぐにわかります。

46
Aaron Digulla

xxhashは、それ自体が非常に高速で強力であり、衝突に関するものであると主張しています。

http://cyan4973.github.io/xxHash/

32ビットプロセッサでは低速ですが、32ビットプロセッサよりも64ビットプロセッサで「さらに高速」に動作する64ビットバリアントがあります(図を参照)。

http://code.google.com/p/crcutil も非常に高速であると言われています(また、存在する場合はハードウェアCRC命令を活用します。これはおそらく非常に高速ですが、ハードウェアがない場合はそれらをサポートしていますが、それほど高速ではありません)。 CRC32cが(衝突に関して)xxHashと同じくらい良いかどうかわからない...

https://code.google.com/p/cityhash/ crcutilに似ており、関連しているようです(指示があれば、ハードウェアCRC32c命令を使用するようにコンパイルできるという点で)。

「最速の生の速度だけが必要」で、ハッシュ出力のランダム分布の品質をあまり気にしない場合(たとえば、小さなセットの場合、または速度が重要な場合)、ここで言及されているいくつかの高速アルゴリズムがあります。 http://www.sanmayce.com/Fastest_Hash/ (これらの「ランダムではない」分布型アルゴリズムは、場合によっては「十分」で非常に高速です。どうやらFNV1A_Jesteressは、「長い」文字列の場合は最速で、小さな文字列の場合は最速です。 http://locklessinc.com/articles/fast_hash/ も関連しているようです。私はこれらの衝突特性が何であるかを調べるために研究しませんでした。

19
rogerdpack

MurmurHash を試すことができます。これは、高速になるように特別に設計されており、コーディングが非常に簡単です。念のために、MurmurHashが一致を返す場合は、2番目のより安全なハッシュを使用することもできます。

3
int3

このタイプのアプリケーションの場合、 Adler32 はおそらく妥当なレベルのセキュリティを備えた最速のアルゴリズムです。大きなファイルの場合、複数のハッシュ値を計算できます。たとえば、ファイルの5 Mbのブロックごとに1つであるため、エラーの可能性が低くなります(つまり、ハッシュが同じでもファイルの内容が異なる場合)。さらに、このマルチハッシュ値の設定により、ハッシュの計算をマルチスレッド方式で実装できます。

Edit:(Steven Suditの発言の後)
ファイルが小さい場合の注意事項!
Adler32の「暗号化」プロパティ、またはむしろその短所は、特にショートメッセージでよく知られています。このため、数キロバイト未満のファイルについては、提案されたソリューションを避ける必要があります。
それでも、この質問では、OPは明示的に高速アルゴリズムおよびセキュリティに関する懸念を放棄を探します。さらに、スピードを追求することは、小さなファイルではなく「大きな」ファイルを処理しているを意味する可能性があります。このコンテキストでは、おそらく5Mbのファイルチャンクに並行して適用されるAdler32が非常に有効な答えのままです。 Alder32は、そのシンプルさとスピードで評価されています。また、その信頼性は、同じ長さのCRCの信頼性よりも低いままですが、4000バイトを超えるメッセージにはまったく受け入れられます。

3
mjv

両方のファイルを読み取って両方のハッシュを生成する必要があることを考えると、それが1つだけの場合、一度に少量ずつそれぞれを読んで比較してみませんか?

それに失敗すると [〜#〜] crc [〜#〜] は非常に単純なアルゴリズムです。

2
Jeff Foster

ここで最適化しているのは、タスクに費やした時間です。残念ながら、最適なソリューションが何であるかを知るために、手元のタスクについて十分な知識がありません。

2つの任意のファイルの1回限りの比較ですか?次に、サイズを比較し、その後、IOに適している場合は、バイト単位(またはmb単位)でファイルを単純に比較します。

2つの大きなファイルセット、または多くのファイルセットの場合で、1回限りの演習ではありません。しかし、頻繁に発生することになる場合は、各ファイルのハッシュを保存する必要があります。ハッシュは決して一意ではありませんが、約9桁(32ビット)の数のハッシュは約40億の組み合わせに対して適切であり、64ビットの数は16 * 10 ^ 18の5つの異なるファイルを区別するのに十分です。

適切な妥協案は、各ファイルに2つの32ビットハッシュを生成することです。最初の8kに1つ、1MB + 8kに1つ、それらを1つの64ビット数としてまとめます。既存のすべてのファイルをDBにカタログ化するのはかなり迅速で、このDBに対して候補ファイルを検索するのも非常に高速です。一致した場合、それらが同じかどうかを判断する唯一の方法は、ファイル全体を比較することです。

私は人々に必要なものを提供することを信じています。それは必ずしも彼らが必要だと思うもの、または何が欲しいのかということではありません。

2
bjorn

なぜあなたはそれをハッシュしたいのですか?

2つのファイルが等しいことを確認したい場合は、定義上、ファイル全体を読み取る必要があります(ファイルが文字通り同じファイルである場合を除き、この場合、ファイルシステムのメタデータを調べることで確認できます)。とにかく、ハッシュする理由はありません。それらを読んで、同じかどうかを確認してください。ハッシュすると効率が低下します。ハッシュが一致しても、ファイルが本当に等しいかどうかはまだわかりません。

編集:この回答は、質問がネットワークに関する何かを指定する前に投稿されました。 2つのファイルの比較について質問しただけです。ファイル間にネットワークホップが存在することがわかったので、MD5ハッシュを使用して完了です。

1
tster

以下は、個人プロジェクトから重複ファイルを見つけて、重複を削除する画像を並べ替えるコードです。私の経験によれば、最初にCRC32のような高速ハッシュアルゴリズムを使用し、次にMD5またはSHA1を実行するとさらに遅くなり、同じサイズのほとんどのファイルが実際に複製されたため、CPU時間の観点から2回のハッシュの実行はより高価でしたので、改善されませんでした、このアプローチはすべてのタイプのプロジェクトに適しているわけではありませんが、画像ファイルには間違いなく当てはまります。ここでは、同じサイズのファイルでのみMD5またはSHA1ハッシュを実行しています。

PS:効率的にハッシュを生成するためにApache commonsコーデックに依存します。

使用例:new DuplicateFileFinder( "MD5")。findDuplicateFilesList(filesList);

    import Java.io.File;
    import Java.io.FileInputStream;
    import Java.io.IOException;
    import Java.util.ArrayList;
    import Java.util.Collection;
    import Java.util.HashMap;
    import Java.util.Iterator;
    import Java.util.List;
    import Java.util.Map;

    import org.Apache.commons.codec.digest.DigestUtils;

    /**
     * Finds the duplicate files using md5/sha1 hashing, which is used only for the sizes which are of same size.
     *  
     * @author HemantSingh
     *
     */
    public class DuplicateFileFinder {

        private HashProvider hashProvider;
        // Used only for logging purpose.
        private String hashingAlgo;

        public DuplicateFileFinder(String hashingAlgo) {
            this.hashingAlgo = hashingAlgo;
            if ("SHA1".equalsIgnoreCase(hashingAlgo)) {
                hashProvider = new Sha1HashProvider();
            } else if ("MD5".equalsIgnoreCase(hashingAlgo)) {
                hashProvider = new Md5HashProvider();
            } else {
                throw new RuntimeException("Unsupported hashing algorithm:" + hashingAlgo + " Please use either SHA1 or MD5.");
            }
        }

        /**
         * This API returns the list of duplicate files reference.
         * 
         * @param files
         *            - List of all the files which we need to check for duplicates.
         * @return It returns the list which contains list of duplicate files for
         *         e.g. if a file a.JPG have 3 copies then first element in the list
         *         will be list with three references of File reference.
         */
        public List<List<File>> findDuplicateFilesList(List<File> files) {
            // First create the map for the file size and file reference in the array list.
            Map<Long, List<File>> fileSizeMap = new HashMap<Long, List<File>>();
            List<Long> potDuplicateFilesSize = new ArrayList<Long>();

            for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) {
                File file = (File) iterator.next();
                Long fileLength = new Long(file.length());
                List<File> filesOfSameLength = fileSizeMap.get(fileLength);
                if (filesOfSameLength == null) {
                    filesOfSameLength = new ArrayList<File>();
                    fileSizeMap.put(fileLength, filesOfSameLength);
                } else {
                    potDuplicateFilesSize.add(fileLength);
                }
                filesOfSameLength.add(file);
            }

            // If we don't have any potential duplicates then skip further processing.
            if (potDuplicateFilesSize.size() == 0) {
                return null;
            }

            System.out.println(potDuplicateFilesSize.size() + " files will go thru " + hashingAlgo + " hash check to verify if they are duplicate.");

            // Now we will scan the potential duplicate files, and eliminate false positives using md5 hash check.
            List<List<File>> finalListOfDuplicates = new ArrayList<List<File>>();
            for (Iterator<Long> potDuplicatesFileSizeIterator = potDuplicateFilesSize
                    .iterator(); potDuplicatesFileSizeIterator.hasNext();) {
                Long fileSize = (Long) potDuplicatesFileSizeIterator.next();
                List<File> potDupFiles = fileSizeMap.get(fileSize);
                Map<String, List<File>> trueDuplicateFiles = new HashMap<String, List<File>>();
                for (Iterator<File> potDuplicateFilesIterator = potDupFiles.iterator(); potDuplicateFilesIterator
                        .hasNext();) {
                    File file = (File) potDuplicateFilesIterator.next();
                    try {
                        String md5Hex = hashProvider.getHashHex(file);
                        List<File> listOfDuplicatesOfAFile = trueDuplicateFiles.get(md5Hex);
                        if (listOfDuplicatesOfAFile == null) {
                            listOfDuplicatesOfAFile = new ArrayList<File>();
                            trueDuplicateFiles.put(md5Hex, listOfDuplicatesOfAFile);
                        }
                        listOfDuplicatesOfAFile.add(file);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                Collection<List<File>> dupsOfSameSizeList = trueDuplicateFiles.values();
                for (Iterator<List<File>> dupsOfSameSizeListIterator = dupsOfSameSizeList.iterator(); dupsOfSameSizeListIterator
                        .hasNext();) {
                    List<File> list = (List<File>) dupsOfSameSizeListIterator.next();
                    // It will be duplicate only if we have more then one copy of it.
                    if (list.size() > 1) {
                        finalListOfDuplicates.add(list);
                        System.out.println("Duplicate sets found: " + finalListOfDuplicates.size());
                    }
                }
            }

            return finalListOfDuplicates;
        }

        abstract class HashProvider {
            abstract String getHashHex(File file) throws IOException ;
        }

        class Md5HashProvider extends HashProvider {
            String getHashHex(File file) throws IOException {
                return DigestUtils.md5Hex(new FileInputStream(file));
            }
        }
        class Sha1HashProvider extends HashProvider {
            String getHashHex(File file) throws IOException {
                return DigestUtils.sha1Hex(new FileInputStream(file));
            }
        }
    }
1
Hemant

いずれの場合も、各ファイルを完全に読み取る必要があります(サイズが一致しない場合を除く)ので、両方のファイルを読み取り、ブロックごとに比較します。

ハッシュを使用すると、CPU使用量が増えるだけです。何も書いていないので、OSのキャッシュは読んだデータを効果的に削除するので、Linuxでは cmp tool

1
socketpair

Zmodemのような古いモデム転送プロトコルは、送信されたブロックごとに何らかのCRC比較を行うことを覚えています。 CRC32、古代の歴史をよく覚えているなら。それがあなたがしていることそのものでない限り、あなたがあなた自身の転送プロトコルを作ることをお勧めしません処理するプロセッサ。自分で試したことがありません。

0
Ricochetv1

samba/rsync開発者が使用するアルゴリズムをチェックアウトできます。私はそれを深く見ていませんが、私はそれがいつも言及されているのを見ます。どうやらかなり良い。

0
clarson