Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C+中的高效循环缓冲区+;它将传递给C样式数组函数参数_C++_Arrays_Circular Buffer - Fatal编程技术网

C++ C+中的高效循环缓冲区+;它将传递给C样式数组函数参数

C++ C+中的高效循环缓冲区+;它将传递给C样式数组函数参数,c++,arrays,circular-buffer,C++,Arrays,Circular Buffer,我正在寻求关于我解决以下问题的方法的建议。我有一个需要添加到缓冲区的恒定数据输入,在每次迭代中,我需要将缓冲数据传递给一个函数,该函数通过指针接受C样式的数组 我担心效率,所以我思考如何在某种循环缓冲区中存储和管理数据,同时将其作为顺序原始数据传递给上述函数 我目前的方法可以总结为以下示例: #include <iostream> #include <array> #include <algorithm> void foo(double* arr, int

我正在寻求关于我解决以下问题的方法的建议。我有一个需要添加到缓冲区的恒定数据输入,在每次迭代中,我需要将缓冲数据传递给一个函数,该函数通过指针接受C样式的数组

我担心效率,所以我思考如何在某种循环缓冲区中存储和管理数据,同时将其作为顺序原始数据传递给上述函数

我目前的方法可以总结为以下示例:

#include <iostream>
#include <array>
#include <algorithm>

void foo(double* arr, int size)
{
  for (uint k = 0; k < size; k++)
    std::cout << arr[k] << ", ";

  std::cout << std::endl;
}

int main()
{
  const int size = 20;
  std::array<double, size> buffer{};

  for (double data = 0.0; data < 50.0; data += 1.0)
  {
      std::move(std::next(std::begin(buffer)), std::end(buffer), std::begin(buffer));
      buffer.back() = data;

      foo(buffer.data(), size);
  }
}
#包括
#包括
#包括
void foo(双*arr,整数大小)
{
对于(uint k=0;kstd::cout您将始终必须复制数据,因为“连续”的环形缓冲区并不存在(可能在某些奇特的硅中确实存在)

此外,无法初始化运行时定义大小的数组模板

您可以使用向量来实现这一点:

#include <iostream>
#include <chrono>
#include <deque>
#include <vector>

int main() {

    std::vector<double> v;

    // pre fill it a little
    for(double data = 0.0; data > -50000.0; data -= 1.0) {
        v.push_back(data);
    }

    size_t cnt = 0;
    int duration = 0;
    int max = 0;

    for(double data = 0.0; data < 50000.0; data += 1.0, ++cnt) {

        auto t1 = std::chrono::high_resolution_clock::now();

        v.push_back(data);
        v.erase(v.begin());

        // foo(v.data(), v.size());

        auto t2 = std::chrono::high_resolution_clock::now();
        auto delta = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();
        duration += delta;

        if(max == 0 || max < delta) {
            max = delta;
        }

    }

    std::cout << "it took an average of " << duration / cnt << "us and a max of " << max << " us" << std::endl;

    return 0;
}

谢谢您的回答。当我在Repl.it上运行此解决方案时,我得到:

it took an average of 21us and a max of 57382us
为了进行比较,我最初的想法是使用相同的缓冲区大小,结果如下:

it took an average of 19us and a max of 54129us
这意味着我最初的做法确实很幼稚:)

与此同时,在等待答案的同时,我提出了以下解决方案:

#include <iostream>
#include <array>
#include <algorithm>
#include <chrono>

void foo(double* arr, int size)
{
  for (uint k = 0; k < size; k++)
    std::cout << arr[k] << ", ";

  std::cout << std::endl;
}

int main()
{
  const int buffer_size = 20;
  std::array<double, buffer_size*2> buffer{};
  int buffer_idx = buffer_size;

  for (double data = 0.0; data < 100.0; data += 1.0)
  {
    buffer.at(buffer_idx - buffer_size) = data;
    buffer.at(buffer_idx++) = data;

    foo(buffer.data() + buffer_idx - buffer_size, buffer_size);

    buffer_idx -= buffer_size * (buffer_idx == buffer_size * 2);
  }
}
除了stribor14之外,我还有另外两个建议。这些建议仅基于性能,因此在这里找不到可读或可维护的代码

在阅读问题时,我的第一个想法也是分配两倍的存储量,但只写入一次。当写入所有位置时,后半部分将复制到上半部分。我的第一直觉说,这可能是一个更好的性能。我的推理是,总写入次数相同,但所有写入都是s顺序(而不是每秒钟跳一次写入到数组中的另一个位置)

现在,我注意到我正在进入C++优化的兔子洞。因此,我们不能到此为止。为了选择要使用的实现,我想运行一个基准测试。Werner Pirkl给出了一个答案。但是在我们优化的代码上运行这个是毫无意义的,因为测量的时间是0μs。所以让我们稍微改变一下。我在ben中编写了一个循环chmark给了它一些运行时间,并提出:

const int repeats = 1000;
volatile double *ptr;
int duration = 0;
const size_t buffer_size = 50'000;

// ... Set up of the buffers and indices

for (int i = 0; i < repeats; ++i)
{
    auto t1 = std::chrono::high_resolution_clock::now();

    for (double data = 0.0; data < 10 * buffer_size; data += 1.0)
    {
        // ... add data to circular buffer

        ptr = // ... the start of the array
    }

    auto t2 = std::chrono::high_resolution_clock::now();
    duration += std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
}

不用说:我对我认为应该表现最好的东西感到非常失望。但正如前面所说的,这个基准并不能代表任何现实世界的性能。

事实证明,大多数计算机都有必要的花式硅来实现连续环形缓冲区,只要缓冲区的大小是页面大小的倍数ze.参见示例:我在结尾中实施的确切方法如果你真的想要性能,你应该用
[]替换te
.at
运算符。请参阅。在我对您的代码进行的测量中,它提供了大约20%到40%的加速。这听起来像下面提到的虚拟环形缓冲区G.Sliepen;)很好,我学到了一些东西,我用
-O3-march=native
编译,因为代码的其余部分(这是其中的一部分)使用矢量化。谢谢您的回答另外,我后来测量的平均时间大约是
130ns
it took an average of 0us and a max of 23us
#include <cstddef>
#include <cstring>
#include <array>

const size_t buffer_size = 50'000;

int main()
{
    std::array<double, 2 * buffer_size> buffer{};
    double *index = buffer.data();
    double *mid = index + buffer_size;

    for (double data = 0.0; data < 10 * buffer_size; data += 1.0)
    {
        if (index == mid)
        {
            index = buffer.data();
            std::memcpy(index, mid, buffer_size * sizeof(double));
        }

        *(index++ + buffer_size) = data;

        foo(index, buffer_size);
    }
}
#include <cstddef>
#include <array>

const size_t buffer_size = 50'000;

int main()
{
    std::array<double, buffer_size * 2> buffer{};
    double *index = buffer.data();
    double *mid = buffer.data() + buffer_size;

    for (double data = 0.0; data < 10 * buffer_size; data += 1.0)
    {
        *index = data;
        *(index + buffer_size) = data;
        ++index;

        index -= buffer_size * (index == mid);

        foo(index, buffer_size);
    }
}
const int repeats = 1000;
volatile double *ptr;
int duration = 0;
const size_t buffer_size = 50'000;

// ... Set up of the buffers and indices

for (int i = 0; i < repeats; ++i)
{
    auto t1 = std::chrono::high_resolution_clock::now();

    for (double data = 0.0; data < 10 * buffer_size; data += 1.0)
    {
        // ... add data to circular buffer

        ptr = // ... the start of the array
    }

    auto t2 = std::chrono::high_resolution_clock::now();
    duration += std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
}
                     with `memcpy`   stribor14   `operator[]`   with pointers 
                   |---------------|-----------|--------------|---------------|
               -O2 |         1.38  |     1.57  |        1.41  |         1.15  |
               -O3 |         1.37  |     1.63  |        1.36  |         1.09  |
 -O3 -march=native |         1.35  |     1.61  |        1.34  |         1.09  |