2017-12-05 28 views
1

私は可変引数パック拡張せずに関数の呼び出しと同じバッファをデコードする対可変長テンプレート引数を持つ関数の呼び出しでバッファをデコードする間のパフォーマンスを測定するためのクイックベンチを(下のリンクを参照)を使用。ベンチマーク可変引数テンプレート関数呼び出し

他の実装と同じようにvariadic実装を作成する方法についてのアイデアはありますか?

ベンチマークの結果は、(CPU時間/無駄時間)の割合です。ベンチマークは、負荷が不明なAWSマシンのプール上で実行されます。目的は、同じ条件で実行されるコードの2つのスニペットを合理的に比較することです。非可変テンプレート関数のCPU時間は5.9であり、可変実装の場合は21.3でした。コンパイラ:最適化レベルO3のClang 5.0。

Benchmark

#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); 

Quick-Bench

+0

あなたはどのプラットフォームにいますか?あなたのコンパイラがx86-64 System V呼び出し規約(WindowsやLinuxやMacOSではなく)をターゲットにしている場合、可変長関数はやや高価です(ただし、可変ではない関数がWindowsの呼び出し規約これは通常の関数を犠牲にして可変関数に最適化されています。) –

+0

質問を自己充分にしてください。私。いくつかの外部リソースを確認する必要なしに答えることができます。外部リンクが無効になる可能性があります。 – bolov

+0

@bolov十分に良いですか? – 0xBADF00

答えて

2

あなたは可変引数の実装への再帰呼び出しを持っていると完璧な転送を使用する場合は、あなたがより良い性能を持つことができます

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)...); 
    } 

See there

+0

ありがとう!私はそれが再現的であると考えたこともない。それはパフォーマンスがずっと悪いと思ったからだ。 – 0xBADF00

関連する問題