Вызывает ли std::bit_cast накладные расходы?

Вопрос или проблема

Я ищу законные примеры использования std::bit_cast в своих кодовых базах. Однако текущая документация cppreference заставляет меня задуматься, может ли это вызвать накладные расходы из-за создания (именованного) временного объекта (и, таким образом, не подлежащего обязательному исключению копирования/перемещения).

Если посмотреть на стандарт, то он также утверждает, что возвращает простой объект, который должен быть скопирован.

Действительно ли есть накладные расходы, каковы преимущества по сравнению с простым std::memcpy для существующего объекта?

В качестве иллюстрации вот вымышленный пример, где, насколько я понимаю, std::bit_cast подразумевает значительно больше ассемблера, чем std::memcpy (это не лучший критерий, но я не смог найти более простой способ проиллюстрировать свой вопрос):

//header.h
#ifndef HEADER
#define HEADER
#include <array>

class S {
   public:
    int val[1024] = {-1};
};

void SetBC(S& s,const std::array<char,sizeof(S)>& arr);
void SetMC(S& s,const std::array<char,sizeof(S)>& arr);

#endif

// main.cpp
#include "header.h"
#include <bit>
#include <cstring>

void SetBC(S& s,const std::array<char,sizeof(S)>& arr)
{
    s = std::bit_cast<S>(arr);
}
void SetMC(S& s,const std::array<char,sizeof(S)>& arr)
{
    std::memcpy(&s,arr.data(),sizeof(S));
}

Ответ или решение

Использование std::bit_cast в C++ действительно вызывает некоторые вопросы относительно производительности и потенциальных накладных расходов. Разберёмся в этом подробнее.

Обзор std::bit_cast

std::bit_cast — это функция, представленная в C++20, предназначенная для безопасного и эффективного преобразования между типами, не изменяя при этом представление данных. Она позволяет интерпретировать память одного типа как память другого типа, что может быть полезно в ряде случаев, например, при низкоуровневой работе с памятью и при реализации сериализации.

Накладные расходы std::bit_cast

  1. Временные объекты:
    Согласно стандарту, std::bit_cast выполняет копирование результата в возвращаемый объект. Это значит, что если возвращаемый тип не является тривиальным (например, класс с пользовательскими конструкторами), это может привести к дополнительным накладным расходам на его копирование, если пространство для хранения результата не может быть использовано напрямую без создания временного объекта.

  2. Проверка типов:
    В отличие от std::memcpy, которое просто копирует байты между двумя участками памяти, std::bit_cast выполняет строгую проверку на совместимость типов. Это требует дополнительных операций времени выполнения, например, проверки на соответствие размеров и возможных ограничений на типы, что может добавить накладные расходы.

  3. Эффект оптимизации компилятора:
    Если компилятор не может применить оптимизацию копирования, то накладные расходы станут ощутимыми. Например, если копирование не может использоваться при оптимизации (например, копирование не может быть устранено), std::bit_cast может привести к более сложному ассемблерному коду по сравнению с использованием std::memcpy.

Сравнение с std::memcpy

Использование std::memcpy может оказаться более эффективным для копирования простых структур данных или массивов, так как оно отрабатывает быстрее и не требует дополнительных проверок. Однако при этом стоит помнить о следующих моментах:

  • Безопасность:
    std::bit_cast предоставляет дополнительные гарантии безопасности, в то время как std::memcpy не проверяет типы и может привести к неопределённому поведению при неправильном использовании.

  • Семантика:
    std::bit_cast предназначен для более семантически правильного и безопасного преобразования типов, поэтому в контексте современного программирования предпочтительно использовать его.

Практические примеры

На практике, если ваше приложение требует высокой производительности и работа с простыми типами, например, массивами или структурами без пользовательских методов, то использование std::memcpy может оказаться более подходящим выбором. Однако, если безопасность типов и правильность преобразований более важны, особенно в коде, который будет поддерживаться долгое время, то std::bit_cast может быть более разумным выбором.

Заключение

Таким образом, накладные расходы, связанные с использованием std::bit_cast, могут различаться в зависимости от контекста. В общем случае, при загрузке обычных типов или простых структур лучше использовать std::memcpy, если вы можете гарантировать, что типы совместимы и безопасны для копирования. В противном случае, для более сложных и безопасных преобразований предпочтителен std::bit_cast, даже если это может привести к некоторым дополнительным накладным расходам.

Оцените материал
Добавить комментарий

Капча загружается...