Надежный и быстрый алгоритм контрольной суммы?

Какой алгоритм контрольной суммы вы можете порекомендовать в следующем сценарии использования?

Я хочу сгенерировать контрольные суммы небольших файлов JPEG (~ 8 КБ каждый), чтобы проверить, изменилось ли содержимое. К сожалению, использование Дата изменена файловой системы не вариант. Контрольная сумма не нужно должна быть криптографически надежной, но она должна надежно указывать на изменения любого размера.

Второй критерий - скорость, поскольку должна быть возможность обрабатывать не менее сотни изображений в секунду (на современном ЦП).

Расчет будет производиться на сервере с несколькими клиентами. Клиенты отправляют изображения на сервер через Gigabit TCP. Итак, узким местом является нет дискового ввода / вывода.

1 гигабит - это 125 мегабайт (полный дуплекс). Из этих 125 МБ / с очень значительная часть будет приходиться на сетевые издержки (особенно если вы хотите отправить сотни небольших сообщений). Поскольку MD5 на небольшом ядре может работать со скоростью почти 250 МБ / с, вам следует изменить его с отсутствие дискового ввода-вывода как узкого места на что-то вроде полностью дисковый ввод-вывод как узкое место. Этот вопрос поднимался уже 7 лет, и никто, кажется, не упомянул, что если бы вы только попробовали что-то перед публикацией в StackOverflow, вы бы сами в этом убедились.

J.J 01.03.2016 17:42

Насколько я понимаю, сервер мог предварительно вычислить контрольную сумму всех своих файлов. Таким образом, ему вообще не нужно было бы выполнять дисковый ввод-вывод? Ему нужно только читать файлы, отправленные клиентами, и проверять их контрольную сумму и сравнивать с заранее рассчитанными контрольными суммами?

avl_sweden 07.08.2020 10:05
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
37
2
34 662
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Ответ принят как подходящий

Если у вас много маленьких файлов, узким местом будет файловый ввод-вывод, а не алгоритм контрольной суммы.

Список хэш-функций (который можно рассматривать как контрольную сумму) можно найти в здесь.

Есть ли причина, по которой вы не можете использовать дату изменения файловой системы, чтобы определить, был ли изменен файл? Вероятно, это было бы быстрее.

Согласовано. Большинство не криптографически безопасных хэш-алгоритмов достаточно короткие, поэтому существует значительная вероятность ложноотрицательных результатов, поэтому я бы просто использовал MD5 или SHA1 и покончил с этим. Скорость вычислений вряд ли будет проблемой.

Nick Johnson 24.09.2008 11:44

@luke Я также хочу проверить модификацию файлов, и в последний раз, когда я просматривал свой архив, я заметил, что несколько процентов файлов в моем архиве (небольшой архив 100 ГБ на двух разных дисках) повреждены! Отметка времени не была изменена, но содержимое файла было незаметно повреждено. Некоторым файлам более 10 лет, поэтому я должен был перемещать их как минимум дважды с одной машины на другую. Так что контрольная сумма обязательна.

bartosz.r 06.02.2012 19:44

CRC

CRC не является криптографически стойким

sergtk 23.09.2008 23:01

Что делает его подходящим для этого приложения.

erickson 23.09.2008 23:12

OP не запрашивал криптографически стойкий алгоритм хеширования :)

radu_c 23.09.2008 23:13
  • CRC-32 приходит на ум в основном потому, что его дешево вычислить

  • Любой вид ввода-вывода приходит на ум в основном потому, что он будет ограничивающим фактором для такого предприятия;)

  • Проблема не в вычислении контрольных сумм, проблема в том, чтобы загрузить изображения в память для вычисления контрольной суммы.

  • Я бы посоветовал «пошаговый» мониторинг:

    • этап 1: проверьте наличие изменений временных меток файла и, если вы обнаружите изменение, передайте на ...
      (не требуется в вашем случае, как описано в отредактированной версии)

    • этап 2: забираем изображение в память и вычисляем контрольную сумму

  • Конечно, также важно: многопоточность: настройка конвейера, который позволяет обрабатывать несколько изображений параллельно, если доступно несколько ядер ЦП.

Есть много быстрых алгоритмов CRC, которые должны помочь: http://www.google.com/search?hl=en&q=fast+crc&aq=f&oq=

Редактировать: Почему ненавидят? CRC полностью уместен, о чем свидетельствуют другие ответы. Поиск в Google также был уместен, поскольку язык не был указан. Это старая, старая проблема, которую решали столько раз, что вряд ли можно будет дать окончательный ответ.

При достаточном количестве файлов коллизия становится очень вероятной. Посмотрите «парадокс дня рождения». Для CRC32 вам нужно всего около 100 000 изображений, чтобы коллизия была высоковероятной.

avl_sweden 07.08.2020 10:09

@avl_sweden не уверен, почему вы направили этот комментарий мне. Судя по комментариям автора вопроса, цель состоит в том, чтобы обнаружить повреждение файла, а не дедупликацию, поэтому повторяющиеся CRC не имеют значения. Это именно та проблема, для решения которой была изобретена CRC.

Mark Ransom 07.08.2020 17:34

Хорошая точка зрения! Примите мои извинения за недоразумение!

avl_sweden 08.08.2020 22:59

adler32, доступный в заголовках zlib, рекламируется как значительно более быстрый, чем crc32, но лишь немного менее точный.

Контрольная сумма Adler очень слабая для небольших объемов данных, менее нескольких сотен байт. Протокол SCTP изначально использовал Adler-32 в качестве контрольной суммы, но по этой причине был вынужден перейти на CRC-32 (в RFC 3309). Хотя средний размер опрашиваемых составляет 8 КБ, для самых маленьких файлов это может быть проблемой.

DGentry 23.09.2008 23:23

CRC32, вероятно, достаточно хорош, хотя есть вероятность маленький, что вы можете получить коллизию, так что файл, который был изменен, может выглядеть так, как будто его не было, потому что две версии генерируют одинаковую контрольную сумму. Поэтому, чтобы избежать этой возможности, я предлагаю использовать MD5, который легко будет достаточно быстрым, а вероятность возникновения коллизии снижена до точки, где она почти бесконечно мала.

Как уже говорили другие, с большим количеством небольших файлов вашим настоящим узким местом в производительности будет ввод-вывод, поэтому проблема заключается в этом. Если вы опубликуете еще несколько деталей, кто-то, вероятно, предложит способ разобраться и в этом.

Ваше самое важное требование - «проверить, не изменилось ли содержимое».

Если наиболее важно, чтобы было обнаружено ЛЮБОЕ изменение в файле, вам следует выбрать MD-5, SHA-1 или даже SHA-256.

Учитывая, что вы указали, что контрольная сумма НЕ является криптографически хорошей, я бы рекомендовал CRC-32 по трем причинам. CRC-32 дает хорошие расстояния для файла 8K. CRC-32 будет вычисляться как минимум на порядок быстрее, чем MD-5 (ваше второе требование). Иногда важно, что CRC-32 требует только 32 бита для хранения сравниваемого значения. MD-5 требует в 4 раза больше памяти, а SHA-1 - в 5 раз больше.

Кстати, любой метод будет усилен, если при вычислении хеша указать длину файла.

Согласно Wiki страница, на который указал Люк, MD5 на самом деле быстрее CRC32!

Я сам пробовал это, используя Python 2.6 в Windows Vista, и получил тот же результат.

Вот некоторые результаты:

crc32: 162,481544276 Мбит / с md5: 224,489791549 Мбит / с

crc32: 168.332996575 Мбит / с md5: 226.089336532 Мбит / с

crc32: 155,851515828 Мбит / с md5: 194.943289532 Мбит / с

Я тоже думаю над тем же вопросом, и у меня возникает соблазн использовать вариант Adler-32 Rsync для обнаружения различий в файлах.

Это необычно. Является ли качество реализации CRC32 и MD5 эквивалентом, или один использует все уловки оптимизации компилятора и таблицы поиска, а другой нет?

Ants 25.03.2010 02:59

Просто постскриптум к вышесказанному; jpeg использует сжатие с потерями, и степень сжатия может зависеть от программы, используемой для создания jpeg, цветовой палитры и / или битовой глубины в системе, гаммы дисплея, видеокарты и установленных пользователем уровней сжатия / настроек цвета. Поэтому сравнение JPEG, созданных на разных компьютерах / платформах или с использованием разного программного обеспечения, будет очень сложно на байтовом уровне.

спасибо за этот комментарий. Я уже учел этот факт.

Benedikt Waldvogel 19.05.2009 12:13

Если вы получаете файлы по сети, вы можете вычислить контрольную сумму при получении файла. Это гарантирует, что вы рассчитаете контрольную сумму, пока данные находятся в памяти. Следовательно, вам не придется загружать их в память с диска.

Я считаю, что если вы примените этот метод, вы увидите почти нулевые накладные расходы в своей системе.

Это процедуры, которые я использую во встроенной системе, которая контролирует контрольную сумму прошивки и прочего.

static const uint32_t crctab[] = {
    0x0,
    0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
    0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
    0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
    0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
    0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
    0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
    0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
    0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
    0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
    0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
    0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
    0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
    0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
    0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
    0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
    0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
    0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
    0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
    0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
    0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
    0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
    0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
    0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
    0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
    0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
    0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
    0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
    0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
    0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
    0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
    0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
    0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
    0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
    0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
    0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
    0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
    0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
    0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
    0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
    0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
    0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
    0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
    0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
    0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
    0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
    0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
    0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
    0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
    0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
    0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
    0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

typedef struct crc32ctx
{
    uint32_t crc;
    uint32_t length;
} CRC32Ctx;


#define COMPUTE(var, ch)    (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]

void crc32_stream_init( CRC32Ctx* ctx )
{
    ctx->crc = 0;
    ctx->length = 0;
}

void crc32_stream_compute_uint32( CRC32Ctx* ctx, uint32_t data )
{
    COMPUTE( ctx->crc, data & 0xFF );
    COMPUTE( ctx->crc, ( data >> 8 ) & 0xFF );
    COMPUTE( ctx->crc, ( data >> 16 ) & 0xFF );
    COMPUTE( ctx->crc, ( data >> 24 ) & 0xFF );
    ctx->length += 4;
}

void crc32_stream_compute_uint8( CRC32Ctx* ctx, uint8_t data )
{
    COMPUTE( ctx->crc, data );
    ctx->length++;
}

void crc32_stream_finilize( CRC32Ctx* ctx )
{
    uint32_t len = ctx->length;
    for( ; len != 0; len >>= 8 )
    {
        COMPUTE( ctx->crc, len & 0xFF );
    }
    ctx->crc = ~ctx->crc;
}

/*** pseudo code ***/
CRC32Ctx crc;
crc32_stream_init(&crc);

while((just_received_buffer_len = received_anything()))
{
    for(int i = 0; i < just_received_buffer_len; i++)
    {
        crc32_stream_compute_uint8(&crc, buf[i]); // assuming buf is uint8_t*
    }
}
crc32_stream_finilize(&crc);
printf("%x", crc.crc); // ta daaa

Другие вопросы по теме