Я объединил два файла ISO в один файл. Оба отдельных файла ISO - это дистрибутивы Linux одного и того же поставщика, но разные версии. В программе, которую я написал (показано ниже), вычисляется объединительный файл в считанных блоках по 512 байт и MD5sum блока. MD5sum хранится в Hashet<String>
. Если обнаружен блок с той же сигнатурой с использованием HashSet
, это записывается.Простой алгоритм поиска дубликатов блоков хуже работает при использовании BloomFilter для поиска
Точно такой же алгоритм также выполняется с использованием BloomFilter до фактического поиска на HashSet
. Поскольку BloomFilter
предоставляет гарантии только на «неконкурентоспособность» и может предоставлять ложные срабатывания при сдерживании, я также просматриваю HashSet, если BloomFilter
сообщает, что ключ может присутствовать уже.
Размер объединенного файла составляет> 1 ГБ, и, следовательно, количество 512-байтовых подписей блоков превышает 1,77 млн. Производительность подхода с использованием BloomFilter
последовательно ~ в шесть раз больше, чем у первого подхода.
Любые причины, почему это может быть так? Что-то не так я здесь сделал?
import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.time.StopWatch;
public class SimpleDedupTrial {
public static void main(String[] args) throws IOException {
int blockSize = 512;
HashSet<String> signatureSet = new HashSet<>();
File f = new File(
"D:\\keshav\\per\\projects\\work-area\\dedup-temp\\merged-iso"
);
FileInputStream fis = new FileInputStream(f);
long length = f.length();
long sizeSaved = 0l;
StopWatch sw = new StopWatch();
int len;
byte[] buffer = new byte[blockSize];
while ((len = fis.read(buffer)) != -1) {
String md5Hex = DigestUtils.md5Hex(buffer);
if (sw.isStopped()) {
sw.start();
}
if (sw.isSuspended()) {
sw.resume();
}
if (signatureSet.contains(md5Hex)) {
sizeSaved += len;
} else {
signatureSet.add(md5Hex);
}
sw.suspend();
}
sw.stop();
fis.close();
System.out.println("Time: "+sw.getTime(TimeUnit.MILLISECONDS));
System.out.println("File size in MB: "+convertToMB(length));
System.out.println("Size saved in MB: "+convertToMB(sizeSaved));
System.out.println("Signature set size: "+signatureSet.size());
System.out.println("Duplicate ratio: "+ ((double)sizeSaved * 100/length));
System.out.println("With Blooom:");
useBloomFilter();
}
private static long convertToMB(long sizeInBytes) {
return sizeInBytes/(1024 * 1024);
}
private static void useBloomFilter() throws IOException {
int blockSize = 512;
Funnel<String> strFunnel = (String t, PrimitiveSink ps) -> {
ps.putString(t, Charsets.US_ASCII);
};
HashSet<String> signatureSet = new HashSet<>();
File f = new File(
"D:\\keshav\\per\\projects\\work-area\\dedup-temp\\merged-iso"
);
FileInputStream fis = new FileInputStream(f);
long length = f.length();
long sizeSaved = 0l;
BloomFilter<String> signatureBloomFilter = BloomFilter.create(
strFunnel, (length/blockSize)
);
StopWatch sw = new StopWatch();
int len;
byte[] buffer = new byte[blockSize];
while ((len = fis.read(buffer)) != -1) {
String md5Hex = DigestUtils.md5Hex(buffer);
if (sw.isStopped()) {
sw.start();
}
if (sw.isSuspended()) {
sw.resume();
}
if (signatureBloomFilter.mightContain(md5Hex)) {
if (!signatureSet.contains(md5Hex)) {
signatureBloomFilter.put(md5Hex);
signatureSet.add(md5Hex);
} else {
sizeSaved += len;
}
} else {
signatureBloomFilter.put(md5Hex);
signatureSet.add(md5Hex);
}
sw.suspend();
}
sw.stop();
fis.close();
System.out.println("Time: "+sw.getTime(TimeUnit.MILLISECONDS));
System.out.println("File size in MB: "+convertToMB(length));
System.out.println("Size saved in MB: "+convertToMB(sizeSaved));
System.out.println("Signature set size: "+signatureSet.size());
System.out.println("Duplicate ratio: "+ ((double)sizeSaved * 100/length));
}
}
Пример вывода:
Time: 819
File size in MB: 1071
Size saved in MB: 205
Signature set size: 1774107
Duplicate ratio: 19.183032558071734
With Blooom:
Time: 4539
File size in MB: 1071
Size saved in MB: 205
Signature set size: 1774107
Duplicate ratio: 19.183032558071734
Для меня вы неправильно понимаете цель «BloomFilter», «BloomFilter» - это легкая структура данных, которую мы используем перед чем-то известным как медленный, как доступ к диску ... и известный как слишком большой для загрузки и просто хранить в памяти в кеше. Здесь вы уже каким-то образом используете кеш (ваш HashSet), если вы можете себе это позволить, вам просто не нужен «BloomFilter». –
Я вижу. В моем случае использования подписи блоков файлов BloomFilters не помогло бы. Это правильно? Предположим, что миллионы подписей блоков файлов уже хранятся в отдельном файле на диске (рассчитан заранее), и я загрузил только соответствующий BloomFilter в память. Всякий раз, когда BloomFilter сообщает, что ключ может существовать, мне все равно придется загрузить все миллионы подписей в память, чтобы убедиться в этом. Я здесь? – Keshav
Если вам нужно загрузить миллионы подписей в память, в вашей архитектуре что-то не так. Вы должны иметь возможность напрямую проверять, существует ли какая-либо подпись. Предположим, что все ваши подписи определены в БД, и вы используете подпись в качестве первичного ключа. Предположим, что все подписи не могут вписаться в вашу память, тогда вы будете использовать BloomFilter, в котором вы первоначально загрузили все свои подписи, вместо того, чтобы запускать запрос, чтобы проверить, существует ли данная подпись, вы сначала проверяете свой BF, если он существует, что позволит вам сократить общее количество запросов. –