Во внутренностях snappy есть условно скомпилированный раздел, который выбирает разыменование reinterpret_cast’ed указатель как лучшая реализация для чтения и записи потенциально невыровненных 16, 32 и 64-битных целых чисел на архитектурах, которые, как известно, поддерживают такие операции (например, x86). Откат для других архитектур состоит в том, чтобы использовать реализация на основе memcpy.
Насколько я понимаю, реализация reinterpret_cast демонстрирует неопределенное поведение, а неопределенное средство поведения clang помечает его.
Что меня удивляет, так это почему бы просто не использовать реализацию, основанную на memcpy? Я ожидал бы, что все, кроме самого сломанного компилятора, будут использовать встроенные функции для реализации этих вызовов memcpy, так как размер известен во время компиляции. Фактически, я ожидал бы идентичный кодген от обеих реализаций на любом современном наборе инструментов.
Тем не менее, я также признаю, что snappy был написан людьми, которые знают, о чем они. Так что это заставляет меня задуматься о том, есть ли еще какое-то преимущество в использовании механизма reinterpret_cast, который перевешивает его неопределенное поведение. Не хотите, чтобы производительность зависела от качества реализации компилятора? Что-то еще я не учел?
Не зная программистов, которые написали этот код, я сомневаюсь, что вы можете получить действительно авторитетный ответ.
Вот мое лучшее предположение: авторы не хотели полагаться на возможное memcpy
оптимизация (которая никоим образом не гарантируется спецификацией, даже если она реализована многими компиляторами). С другой стороны, написание reinterpret_cast
очень, очень вероятно, выдаст просто команду выравнивания доступа, которую ожидали авторы, практически на любом компиляторе.
В то время как умные, современные компиляторы будут оптимизировать memcpy
старшие не могут. Постоянная производительность может быть весьма критичной для этой библиотеки, поэтому они, похоже, пожертвовали некоторой корректностью (так как reinterpret_cast
представляется потенциально UB) в пользу получения более согласованных результатов в более широком наборе компиляторов.
Причина в том, что быстрее (на x86) загрузить int с невыровненного адреса, чем скопировать его и затем загрузить.
Накладные расходы на невыровненную загрузку составляют примерно фактор 2. Memcpy сводится к чтению 4 байта, записи 4 байта (или одной 32-битной записи, в зависимости от компилятора), а затем вам все еще нужна загрузка. В лучшем случае оптимизатор может обнаружить, что запись после чтения является избыточной.
Лично я бы реализовал безопасный метод как 4-байтовые нагрузки со сдвигами.