簡體   English   中英

基准可變參數模板函數調用

[英]Benchmark variadic template function call

我使用了Quick Bench(請參見下面的鏈接)來測量在通過調用帶有可變參數模板函數的函數解碼緩沖區與在不調用帶有可變參數包擴展函數的函數解碼同一緩沖區之間的性能。

關於如何使可變參數實現與其他實現相同的任何想法?

基准測試的結果是(CPU時間/ Noop時間)的比率。 基准測試在負載未知的AWS機器池上運行。 目的是對在相同條件下運行的兩個代碼片段進行合理的比較。 非可變參數模板功能的CPU時間為5.9,可變參數實現的CPU時間為21.3。 編譯器:Clang 5.0,優化級別為O3。

基准

#include <cstdint>
#include <cstring>
#include <string>
#include <type_traits>

namespace core { namespace decoder 
{
   class LittleEndian
   {
   public:
      LittleEndian(const LittleEndian&) = delete;
      LittleEndian& operator=(const LittleEndian&) = delete;

   public:
      constexpr LittleEndian(const std::uint8_t* buffer, size_t size) noexcept
      : m_buffer(buffer),
        m_size(size)
      {}

      constexpr bool decodeU8(
        size_t& offset, std::uint8_t& decodedValue) const noexcept
      {
        if (offset >= m_size)
            return false;

        decodedValue = m_buffer[offset];
        offset += sizeof(std::uint8_t);
        return true;
      }

      constexpr bool decodeU16(
        size_t& offset, std::uint16_t& decodedValue) const noexcept
      {
        if (offset + sizeof(std::uint16_t) > m_size)
            return false;

        const uint8_t b0 = m_buffer[offset], b1 = m_buffer[offset + 1];
        decodedValue = (b0 << 0) | (b1 << 8);
        offset += sizeof(std::uint16_t);
        return true;
      }

      constexpr bool decodeU32(
        size_t& offset, std::uint32_t& decodedValue) const noexcept
      {
        if (offset + sizeof(std::uint32_t) > m_size)
            return false;

        const uint8_t b0 = m_buffer[offset], b1 = m_buffer[offset + 1], b2 = m_buffer[offset + 2], b3 = m_buffer[offset + 3];
        decodedValue = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
        offset += sizeof(std::uint32_t);
        return true;
      }

      constexpr bool decodeU64(
        size_t& offset, std::uint64_t& decodedValue) const noexcept
      {
        if (offset + sizeof(std::uint64_t) > m_size)
            return false;

        const uint8_t b0 = m_buffer[offset], b1 = m_buffer[offset + 1],
            b2 = m_buffer[offset + 2], b3 = m_buffer[offset + 3],
            b4 = m_buffer[offset + 4], b5 = m_buffer[offset + 5],
            b6 = m_buffer[offset + 6], b7 = m_buffer[offset + 7];

        decodedValue = (static_cast<std::uint64_t>(b0) << 0) |
            (static_cast<std::uint64_t>(b1) << 8) |
            (static_cast<std::uint64_t>(b2) << 16) |
            (static_cast<std::uint64_t>(b3) << 24) |
            (static_cast<std::uint64_t>(b4) << 32) |
            (static_cast<std::uint64_t>(b5) << 40) |
            (static_cast<std::uint64_t>(b6) << 48) |
            (static_cast<std::uint64_t>(b7) << 56);

        offset += sizeof(std::uint64_t);
        return true;
      }

  private:
    const std::uint8_t* m_buffer;
    const size_t m_size;
  };

  template<typename EndianDecoderT>
  class ByteDecoder
  {
  public:
    ByteDecoder(const ByteDecoder&) = delete;
    ByteDecoder& operator=(const ByteDecoder&) = delete;

  public:
    constexpr ByteDecoder(const std::uint8_t* buffer, size_t size)
      : m_buffer(buffer),
        m_size(size),
        m_endianDecoder(buffer, size)
    {}

    template<typename ...Args>
    constexpr bool decode(size_t offset, Args&... args) const noexcept
    {
        bool success = true;

        using expand_type = int[];
        expand_type
        {
            ([&success] (auto result) noexcept
            {
                success = (!success || !result) ? false : true;

            } (decodeValue(offset, args)), 0)...
        };

        return success;

      }

      template<typename T>
      constexpr bool decode(size_t offset, T& decodedValue) const noexcept
      {
        return decodeValue(offset, decodedValue);
      }

   private:
    template<typename T>
    constexpr bool decodeValue(
        size_t &offset, T& decodedValue) const noexcept
    {
        if constexpr (std::is_same< std::decay_t<T>, std::uint8_t>::value)
            return m_endianDecoder.decodeU8(offset, decodedValue);

        if constexpr (std::is_same< std::decay_t<T>, std::uint16_t>::value)
            return m_endianDecoder.decodeU16(offset, decodedValue);

        if constexpr (std::is_same< std::decay_t<T>, std::uint32_t>::value)
            return m_endianDecoder.decodeU32(offset, decodedValue);

        if constexpr (std::is_same< std::decay_t<T>, std::uint64_t>::value)
            return m_endianDecoder.decodeU64(offset, decodedValue);

        if constexpr (std::is_same<char *, typename std::decay<T>::type>::value ||
                      std::is_same<char const *, typename std::decay<T>::type>::value)
            return decodeCHR(offset, decodedValue);

        return false;
      }

      template<size_t SIZE>
      constexpr bool decodeCHR(
        size_t &offset, char (&buffer)[SIZE]) const noexcept
      {
        if (offset + SIZE > m_size)
            return false;

        memset(&buffer[0], 0x00, sizeof(char) * SIZE);
        memcpy(&buffer[0], &m_buffer[offset], sizeof(char) * (std::min)(SIZE, std::extent<decltype(buffer)>::value - 1));
        offset += SIZE;
        return true;
      }

  private:
    const std::uint8_t* m_buffer;
    const size_t m_size;
    EndianDecoderT m_endianDecoder;
  };

}} // namespace core::decoder
static void NonVariadicDecoding(benchmark::State& state) {
// Code inside this loop is measured repeatedly
constexpr std::uint8_t littleEndian[] = { 0x0D, 0x0C, 0x84, 0x03, 0x00, 0x00, 'H', 'e', 'l', 'l', 'o', '\0',  0x84, 0x03, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
core::decoder::ByteDecoder<core::decoder::LittleEndian> decoder(littleEndian, sizeof(littleEndian));


for (auto _ : state) {

  size_t offset = 0;

  struct DecodedValue
  {
      std::uint16_t v1_U16;
      std::uint32_t v2_U32;
      char v3_CHR[6];
      std::uint16_t v4_U16;
      std::uint64_t v5_U64;
  };
  DecodedValue dv;

  decoder.decode(offset, dv.v1_U16);  
  offset += sizeof(dv.v1_U16);
  decoder.decode(offset, dv.v2_U32);
  offset += sizeof(dv.v2_U32);
  decoder.decode(offset, dv.v3_CHR);
  offset += sizeof(dv.v3_CHR);
  decoder.decode(offset, dv.v4_U16);
  offset += sizeof(dv.v4_U16);
  decoder.decode(offset, dv.v5_U64);

  benchmark::DoNotOptimize(dv);
  }
}
// Register the function as a benchmark
BENCHMARK(NonVariadicDecoding);
static void VariadicDecoding(benchmark::State& state) {
// Code before the loop is not measured
constexpr std::uint8_t littleEndian[] = { 0x0D, 0x0C, 0x84, 0x03, 0x00, 0x00, 'H', 'e', 'l', 'l', 'o', '\0',  0x84, 0x03, 0x84, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
core::decoder::ByteDecoder<core::decoder::LittleEndian> decoder(littleEndian, sizeof(littleEndian));

for (auto _ : state) {

  struct DecodedValue
  {
      std::uint16_t v1_U16;
      std::uint32_t v2_U32;
      char v3_CHR[6];
      std::uint16_t v4_U16;
      std::uint64_t v5_U64;
  };

  DecodedValue dv;
  decoder.decode(0, dv.v1_U16, dv.v2_U32, dv.v3_CHR, dv.v4_U16, dv.v5_U64);
  benchmark::DoNotOptimize(dv);
  }
}
BENCHMARK(VariadicDecoding);

快速工作台

如果遞歸調用可變參數實現並使用完美的轉發,則可以得到更好的性能:

    template <typename Type>
    constexpr bool decode_impl(size_t offset, Type&& value) const noexcept
    {
      return decodeValue(offset, std::forward<Type>(value));
    }

    template <typename First, typename Second, typename... Other>
    constexpr bool decode_impl(size_t offset, First&& first, Second&& second, Other&&... others) const noexcept
    {
      return decode_impl(offset, std::forward<First>(first)) && decode_impl(offset, std::forward<Second>(second), std::forward<Other>(others)...);
    }

    template<typename ...Args>
    constexpr bool decode(size_t offset, Args&&... args) const noexcept
    {
      return decode_impl(offset, std::forward<Args>(args)...);
    }

看那里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM