我有如下自定義陣列建構式:
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
uint8_t unencrypted_bytes = 10;
我怎樣才能有效地回圈進入這些幀并對其進行處理?只有for回圈是可能的選擇嗎?如果我們只想復制幀而不進行預處理,我知道我們可以使用std::copy. 有什么方法可以讓這個迭代器處理更高效?
// // Copy rest of frame
// std::copy(frame.begin() unencrypted_bytes, frame.begin()
// (encrypted_frame.size() - 41),
// encrypted_frame.begin() unencrypted_bytes);
// Doing XOR for Frame
for (size_t i = unencrypted_bytes; i < encrypted_frame.size() - 41; i ) {
// encrypted_frame[i] = i;
RTC_LOG(LS_INFO) << "Ivan, original frame Before XOR : " << i << " "
<< frame[i];
encrypted_frame[i] = frame[i] ^ fake_key_;
RTC_LOG(LS_INFO) << "Ivan, encrypted frame After XOR : " << i << " "
<< encrypted_frame[i];
}
下面是我的陣列視圖建構式
/*
* Copyright 2015 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef API_ARRAY_VIEW_H_
#define API_ARRAY_VIEW_H_
#include <algorithm>
#include <array>
#include <iterator>
#include <type_traits>
#include "rtc_base/checks.h"
#include "rtc_base/type_traits.h"
namespace rtc {
// tl;dr: rtc::ArrayView is the same thing as gsl::span from the Guideline
// Support Library.
//
// Many functions read from or write to arrays. The obvious way to do this is
// to use two arguments, a pointer to the first element and an element count:
//
// bool Contains17(const int* arr, size_t size) {
// for (size_t i = 0; i < size; i) {
// if (arr[i] == 17)
// return true;
// }
// return false;
// }
//
// This is flexible, since it doesn't matter how the array is stored (C array,
// std::vector, rtc::Buffer, ...), but it's error-prone because the caller has
// to correctly specify the array length:
//
// Contains17(arr, arraysize(arr)); // C array
// Contains17(arr.data(), arr.size()); // std::vector
// Contains17(arr, size); // pointer size
// ...
//
// It's also kind of messy to have two separate arguments for what is
// conceptually a single thing.
//
// Enter rtc::ArrayView<T>. It contains a T pointer (to an array it doesn't
// own) and a count, and supports the basic things you'd expect, such as
// indexing and iteration. It allows us to write our function like this:
//
// bool Contains17(rtc::ArrayView<const int> arr) {
// for (auto e : arr) {
// if (e == 17)
// return true;
// }
// return false;
// }
//
// And even better, because a bunch of things will implicitly convert to
// ArrayView, we can call it like this:
//
// Contains17(arr); // C array
// Contains17(arr); // std::vector
// Contains17(rtc::ArrayView<int>(arr, size)); // pointer size
// Contains17(nullptr); // nullptr -> empty ArrayView
// ...
//
// ArrayView<T> stores both a pointer and a size, but you may also use
// ArrayView<T, N>, which has a size that's fixed at compile time (which means
// it only has to store the pointer).
//
// One important point is that ArrayView<T> and ArrayView<const T> are
// different types, which allow and don't allow mutation of the array elements,
// respectively. The implicit conversions work just like you'd hope, so that
// e.g. vector<int> will convert to either ArrayView<int> or ArrayView<const
// int>, but const vector<int> will convert only to ArrayView<const int>.
// (ArrayView itself can be the source type in such conversions, so
// ArrayView<int> will convert to ArrayView<const int>.)
//
// Note: ArrayView is tiny (just a pointer and a count if variable-sized, just
// a pointer if fix-sized) and trivially copyable, so it's probably cheaper to
// pass it by value than by const reference.
namespace impl {
// Magic constant for indicating that the size of an ArrayView is variable
// instead of fixed.
enum : std::ptrdiff_t { kArrayViewVarSize = -4711 };
// Base class for ArrayViews of fixed nonzero size.
template <typename T, std::ptrdiff_t Size>
class ArrayViewBase {
static_assert(Size > 0, "ArrayView size must be variable or non-negative");
public:
ArrayViewBase(T* data, size_t size) : data_(data) {}
static constexpr size_t size() { return Size; }
static constexpr bool empty() { return false; }
T* data() const { return data_; }
protected:
static constexpr bool fixed_size() { return true; }
private:
T* data_;
};
// Specialized base class for ArrayViews of fixed zero size.
template <typename T>
class ArrayViewBase<T, 0> {
public:
explicit ArrayViewBase(T* data, size_t size) {}
static constexpr size_t size() { return 0; }
static constexpr bool empty() { return true; }
T* data() const { return nullptr; }
protected:
static constexpr bool fixed_size() { return true; }
};
// Specialized base class for ArrayViews of variable size.
template <typename T>
class ArrayViewBase<T, impl::kArrayViewVarSize> {
public:
ArrayViewBase(T* data, size_t size)
: data_(size == 0 ? nullptr : data), size_(size) {}
size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
T* data() const { return data_; }
protected:
static constexpr bool fixed_size() { return false; }
private:
T* data_;
size_t size_;
};
} // namespace impl
template <typename T, std::ptrdiff_t Size = impl::kArrayViewVarSize>
class ArrayView final : public impl::ArrayViewBase<T, Size> {
public:
using value_type = T;
using const_iterator = const T*;
// Construct an ArrayView from a pointer and a length.
template <typename U>
ArrayView(U* data, size_t size)
: impl::ArrayViewBase<T, Size>::ArrayViewBase(data, size) {
RTC_DCHECK_EQ(size == 0 ? nullptr : data, this->data());
RTC_DCHECK_EQ(size, this->size());
RTC_DCHECK_EQ(!this->data(),
this->size() == 0); // data is null iff size == 0.
}
// Construct an empty ArrayView. Note that fixed-size ArrayViews of size > 0
// cannot be empty.
ArrayView() : ArrayView(nullptr, 0) {}
ArrayView(std::nullptr_t) // NOLINT
: ArrayView() {}
ArrayView(std::nullptr_t, size_t size)
: ArrayView(static_cast<T*>(nullptr), size) {
static_assert(Size == 0 || Size == impl::kArrayViewVarSize, "");
RTC_DCHECK_EQ(0, size);
}
// Construct an ArrayView from a C-style array.
template <typename U, size_t N>
ArrayView(U (&array)[N]) // NOLINT
: ArrayView(array, N) {
static_assert(Size == N || Size == impl::kArrayViewVarSize,
"Array size must match ArrayView size");
}
// (Only if size is fixed.) Construct a fixed size ArrayView<T, N> from a
// non-const std::array instance. For an ArrayView with variable size, the
// used ctor is ArrayView(U& u) instead.
template <typename U,
size_t N,
typename std::enable_if<
Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr>
ArrayView(std::array<U, N>& u) // NOLINT
: ArrayView(u.data(), u.size()) {}
// (Only if size is fixed.) Construct a fixed size ArrayView<T, N> where T is
// const from a const(expr) std::array instance. For an ArrayView with
// variable size, the used ctor is ArrayView(U& u) instead.
template <typename U,
size_t N,
typename std::enable_if<
Size == static_cast<std::ptrdiff_t>(N)>::type* = nullptr>
ArrayView(const std::array<U, N>& u) // NOLINT
: ArrayView(u.data(), u.size()) {}
// (Only if size is fixed.) Construct an ArrayView from any type U that has a
// static constexpr size() method whose return value is equal to Size, and a
// data() method whose return value converts implicitly to T*. In particular,
// this means we allow conversion from ArrayView<T, N> to ArrayView<const T,
// N>, but not the other way around. We also don't allow conversion from
// ArrayView<T> to ArrayView<T, N>, or from ArrayView<T, M> to ArrayView<T,
// N> when M != N.
template <
typename U,
typename std::enable_if<Size != impl::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(U& u) // NOLINT
: ArrayView(u.data(), u.size()) {
static_assert(U::size() == Size, "Sizes must match exactly");
}
template <
typename U,
typename std::enable_if<Size != impl::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(const U& u) // NOLINT(runtime/explicit)
: ArrayView(u.data(), u.size()) {
static_assert(U::size() == Size, "Sizes must match exactly");
}
// (Only if size is variable.) Construct an ArrayView from any type U that
// has a size() method whose return value converts implicitly to size_t, and
// a data() method whose return value converts implicitly to T*. In
// particular, this means we allow conversion from ArrayView<T> to
// ArrayView<const T>, but not the other way around. Other allowed
// conversions include
// ArrayView<T, N> to ArrayView<T> or ArrayView<const T>,
// std::vector<T> to ArrayView<T> or ArrayView<const T>,
// const std::vector<T> to ArrayView<const T>,
// rtc::Buffer to ArrayView<uint8_t> or ArrayView<const uint8_t>, and
// const rtc::Buffer to ArrayView<const uint8_t>.
template <
typename U,
typename std::enable_if<Size == impl::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(U& u) // NOLINT
: ArrayView(u.data(), u.size()) {}
template <
typename U,
typename std::enable_if<Size == impl::kArrayViewVarSize &&
HasDataAndSize<U, T>::value>::type* = nullptr>
ArrayView(const U& u) // NOLINT(runtime/explicit)
: ArrayView(u.data(), u.size()) {}
// Indexing and iteration. These allow mutation even if the ArrayView is
// const, because the ArrayView doesn't own the array. (To prevent mutation,
// use a const element type.)
T& operator[](size_t idx) const {
RTC_DCHECK_LT(idx, this->size());
RTC_DCHECK(this->data());
return this->data()[idx];
}
T* begin() const { return this->data(); }
T* end() const { return this->data() this->size(); }
const T* cbegin() const { return this->data(); }
const T* cend() const { return this->data() this->size(); }
std::reverse_iterator<T*> rbegin() const {
return std::make_reverse_iterator(end());
}
std::reverse_iterator<T*> rend() const {
return std::make_reverse_iterator(begin());
}
std::reverse_iterator<const T*> crbegin() const {
return std::make_reverse_iterator(cend());
}
std::reverse_iterator<const T*> crend() const {
return std::make_reverse_iterator(cbegin());
}
ArrayView<T> subview(size_t offset, size_t size) const {
return offset < this->size()
? ArrayView<T>(this->data() offset,
std::min(size, this->size() - offset))
: ArrayView<T>();
}
ArrayView<T> subview(size_t offset) const {
return subview(offset, this->size());
}
};
// Comparing two ArrayViews compares their (pointer,size) pairs; it does *not*
// dereference the pointers.
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
bool operator==(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
return a.data() == b.data() && a.size() == b.size();
}
template <typename T, std::ptrdiff_t Size1, std::ptrdiff_t Size2>
bool operator!=(const ArrayView<T, Size1>& a, const ArrayView<T, Size2>& b) {
return !(a == b);
}
// Variable-size ArrayViews are the size of two pointers; fixed-size ArrayViews
// are the size of one pointer. (And as a special case, fixed-size ArrayViews
// of size 0 require no storage.)
static_assert(sizeof(ArrayView<int>) == 2 * sizeof(int*), "");
static_assert(sizeof(ArrayView<int, 17>) == sizeof(int*), "");
static_assert(std::is_empty<ArrayView<int, 0>>::value, "");
template <typename T>
inline ArrayView<T> MakeArrayView(T* data, size_t size) {
return ArrayView<T>(data, size);
}
// Only for primitive types that have the same size and aligment.
// Allow reinterpret cast of the array view to another primitive type of the
// same size.
// Template arguments order is (U, T, Size) to allow deduction of the template
// arguments in client calls: reinterpret_array_view<target_type>(array_view).
template <typename U, typename T, std::ptrdiff_t Size>
inline ArrayView<U, Size> reinterpret_array_view(ArrayView<T, Size> view) {
static_assert(sizeof(U) == sizeof(T) && alignof(U) == alignof(T),
"ArrayView reinterpret_cast is only supported for casting "
"between views that represent the same chunk of memory.");
static_assert(
std::is_fundamental<T>::value && std::is_fundamental<U>::value,
"ArrayView reinterpret_cast is only supported for casting between "
"fundamental types.");
return ArrayView<U, Size>(reinterpret_cast<U*>(view.data()), view.size());
}
} // namespace rtc
#endif // API_ARRAY_VIEW_H_
uj5u.com熱心網友回復:
這看起來像 WebRTC 代碼。如果我不得不猜測,您正在加密 RTP 資料包的媒體位元組(只是猜測)。所以你可能希望它很快。
我假設您認識到主回圈中的 RTC_LOG 陳述句可能更像是一個回圈性能殺手,而不是您可以做的任何其他事情來優化 xor 加密。如果您記錄每個單獨的位元組,它將否定您所做的任何優化。所以讓我們從這個開始。
for (size_t i = unencrypted_bytes; i < encrypted_frame.size() - 41; i ) {
encrypted_frame[i] = frame[i] ^ fake_key_;
}
for 的運算子多載[]如下所示:
T& operator[](size_t idx) const {
RTC_DCHECK_LT(idx, this->size());
RTC_DCHECK(this->data());
return this->data()[idx];
}
所以這意味著每次迭代都會呼叫data()源陣列和目標陣列。并且operator[]多載會進行一些額外的驗證檢查。在發布版本中,編譯器可能能夠優化大部分內容。但我不知道這一點,因為我不知道編譯器是否會像對 std:: 集合類的操作一樣優化您的 ArrayView。我也不知道那些 RTC_DCHECK 宏是否在發布版本中是無操作的。
但是在除錯版本中,它會非常慢。因此,如果我們可以快速進行除錯,我們可以假設它會延續到您的發布版本。
我們可以確保我們的主回圈遍歷位元組并且不在回圈內進行任何函式呼叫。這將是你最大的加速。因此,這將比您擁有的要快得多:
uint8_t* frame_data = frame.data();
uint8_t* encrypted_data = encrypted_frame.data()
const size_t stop = i < encrypted_frame.size() - 41;
for (size_t i = frame_data unencrypted_bytes; i < stop; i ) {
encrypted_data[i] = frame_data[i] ^ fake_key_;
}
您可以選擇使用 std::transform 而不是 for 回圈,但我認為這幾乎是等效的。
同樣,編譯器完全有可能優化原始函式,使其與我剛剛生成的一樣好。但是由于 ArrayView 沒有為我在本地編譯(手邊沒有 webrtc 源),我不知道。否則,如果可以的話,我會在 Godbolt 上驗證我的所有假設。
但是我確實從經驗中知道,在非常緊密的回圈中迭代位元組或單詞的每個元素的函式呼叫,即使宣告為行內,也永遠不會像手動將所有需要的代碼直接行內到回圈中那樣快。
uj5u.com熱心網友回復:
對于將源范圍異或到目標范圍,我會使用std::transform:
std::transform(
frame.cbegin() unencrypted_bytes,
frame.cend() - 41,
encrypted_frame.begin(),
[=] (const auto byte) -> std::uint8_t { return byte ^ fake_key_; });
在 C 中,通常情況下,使用標準庫通過高級抽象清楚地表達程式的意圖是最佳選擇。源代碼應該用來表達程式需要做什么,并盡可能避免規定如何去做*,因為這會限制編譯器優化提出最好的方法。
* 除非你真的知道你在做什么并且你有基準來證明它
使用 也很好std::bit_xor,但這需要兩個輸入范圍才能呼叫std::transform接受二元運算子的多載。假設常數fake_key_是 a std::uint8_t,下面是一個迭代器的定義,用于模擬一個無限的填充范圍:
template <class T>
struct filled {
using value_type = T;
using difference_type = std::ptrdiff_t;
using reference = const T &;
using pointer = const T *;
using iterator_category = std::input_iterator_tag;
constexpr filled() noexcept = default;
constexpr filled(const filled &) = default;
constexpr filled(filled &&) = default;
constexpr filled(reference value) : value{value} {}
constexpr filled &operator=(const filled &) = default;
constexpr filled &operator=(filled &&) = default;
constexpr ~filled() = default;
constexpr bool operator==(const filled &) const noexcept { return false; }
constexpr reference operator*() const noexcept { return value; }
constexpr pointer operator->() const noexcept {
return std::addressof(value);
}
constexpr filled &operator () noexcept { return *this; }
constexpr filled operator (int) { return *this; }
private:
value_type value;
};
static_assert(std::input_iterator<filled<std::uint8_t>>);
啟用此多載的使用:
std::transform(
frame.cbegin() unencrypted_bytes,
frame.cend() - 41,
encrypted_frame.begin(),
filled<std::uint8_t>(fake_key_),
std::bit_xor<std::uint8_t>());
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/506504.html
