c++ - Linux 上写入共享内存时出现周期性延迟峰值

标签 c++ linux g++ centos7 shared-memory

我有以下代码:

#pragma pack(4)
struct RECORD_HEADER {
uint64_t msgType;
uint64_t rdtsc;
};
struct BODY {
    char content[488];
};
#pragma pack()

class SerializedRDTSC {
public:
    typedef unsigned long long timeunit_t;

    static timeunit_t start(void) {
            unsigned cycles_high, cycles_low;
            __asm__ __volatile__ (  "CPUID\n\t"
                                    "RDTSC\n\t"
                                    "mov %%edx, %0\n\t"
                                    "mov %%eax, %1\n\t": "=r" (cycles_high), "=r" (cycles_low)::
                                    "%rax", "%rbx", "%rcx", "%rdx");
            return ( (unsigned long long)cycles_low)|( ((unsigned long long)cycles_high)<<32 );
    }

    static timeunit_t end(void) {
            unsigned cycles_high, cycles_low;
            __asm__ __volatile__(   "RDTSCP\n\t"
                                    "mov %%edx, %0\n\t"
                                    "mov %%eax, %1\n\t"
                                    "CPUID\n\t": "=r" (cycles_high), "=r" (cycles_low):: "%rax",
                                    "%rbx", "%rcx", "%rdx");
            return ( (unsigned long long)cycles_low)|( ((unsigned long long)cycles_high)<<32 );
    }

};

char* createSHM() noexcept {
        const auto sharedMemHandle = shm_open("testing", O_RDWR | O_CREAT, 0666);
        if (-1 == sharedMemHandle) {
            std::cout << "failed to open named shared memory: " << std::endl;
            return nullptr;
        }
        constexpr int32_t size = (1 << 26);
        ftruncate(sharedMemHandle, size);
        char* ptr = (char*) mmap(nullptr, size, PROT_READ | PROT_WRITE,
                MAP_SHARED, sharedMemHandle, 0);

        if (MAP_FAILED == ptr) {
            std::cout << errno << std::endl;
            return nullptr;
        }

        const auto rc = fchmod(sharedMemHandle, 0666);
        if (rc == -1) {
            fprintf(stderr,
                    "Can't change permissions to 0666 on shared mem segment: %m\n");
            fflush(stderr);
        }
        return ptr;
}

int main() {
    BODY update;

    srand(time(nullptr));
    char* ptr = createSHM();

    constexpr uint64_t n = 700;
    constexpr uint64_t n2 = 10;
    uint64_t m_data[n * n2];
    memset(m_data, 0, sizeof(m_data));

    uint64_t r = 0;

    for (uint64_t i = 0; i < n; i++) {
        for (uint64_t k = 0; k < n2; k++) {
            // populate the header
            const auto msgType = rand();
            const auto rdtsc = rand();

            // populate the struct randomly
            uint32_t* tmp = reinterpret_cast<uint32_t*>(&update);
            for (uint32_t j = 0; j < sizeof(BODY) / sizeof(uint32_t); j++) {
                const uint32_t v = rand() % 32767;
                tmp[j] = v;
            }

            // write the struct
            const auto s = SerializedRDTSC::start();
            memcpy(ptr, (char*)&msgType, sizeof(uint64_t));
            ptr+= sizeof(uint64_t);
            memcpy(ptr, (char*)&rdtsc, sizeof(uint64_t));
            ptr+= sizeof(uint64_t);
            memcpy(ptr, &update, sizeof(BODY));
            ptr+= sizeof(BODY);
            const auto e = SerializedRDTSC::end();
            m_data[r++] = e - s;
        }
        usleep(249998);
    }

    for (uint32_t i = 0; i < r; i++) {
        std::cout << i << "," << m_data[i] << std::endl;
    }
}

由于某种原因,根据输出会出现周期性的延迟峰值:

0   9408
1   210
2   162
3   176
4   172
5   164
6   172
7   8338
8   174
9   186
10  612
11  380
12  380
13  374
14  358
15  13610
16  190
17  186
18  164
19  168
20  246
21  196
22  170
23  5066
24  176
25  176
26  168
27  174
28  166
29  440
30  232
31  214
32  5128
33  180
34  178
35  172
36  174
37  184
38  170
39  162
40  5964
41  182
42  174
43  164
44  180
45  180
46  162
47  172

我已经隔离了核心并使用 htop 进行了双重检查,以确保没有其他进程正在使用该核心。

我的机器有一个 i7 CPU(没什么花哨的)。

然后我尝试使用 Xeon CPU。模式大致相同——每 7-11 次写入,就会出现一个峰值。

使用 i7 CPU,我使用 GCC 7.2 和 c++17 进行编译,并在 CentOS 7.3 上运行。

使用 Xeon CPU,我使用 GCC 4.6 和 c++0x 进行编译,并在 CentOS 6.5 上运行。

我的问题是: 1. 为什么会出现周期性的延迟峰值? (我用strace检查过。我没有看到涉及奇怪的系统调用) 2. 关于如何调查/理解峰值有什么建议吗?更多内容供我学习。

提前致谢!

附注是的,有些人反对使用 rdtsc 来测量延迟,因为温度会影响 TSC。不过,我没有看到任何更好的选择,因为我没有 PTP,并且 Clock_gettime() 有时也会出现延迟峰值。如果您有任何建议,我们非常欢迎:)

最佳答案

一个内存页是4K字节。每次开始在新页面上写入时,该页面都需要映射到进程地址空间。由于每次循环写入的数据为 8 + 8 + 488 = 504 字节,因此循环中每 8 或 9 次就会出现一个峰值。

由于 CPU 可以推测性地从内存中预取数据,因此当硬件预取器尝试访问该页面时,第 2 页的页面错误(应该发生在第 8 个循环上)比预期早了一个循环。

关于c++ - Linux 上写入共享内存时出现周期性延迟峰值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49268637/

相关文章:

c++ - 在 C++ 中,在 for 循环之外使用 "if"还是在 for 循环中使用 "if"效率更高

c++ - 通过套接字发送结构

c++ - 为什么 g++ 似乎混淆了数据类型?

linux - 如果文件存在,如何设置 polkit 规则以锁定关闭?

c++ - SSE g++ 编译问题

c++ - 跨平台 _wtoi() 实现?

c++ - 在 C++ 中,是否有一种最佳方式来运行指向该值的指针链?

c++ - 多维数组 - malloc 与 new

linux - 新安装的 apache 将 html 内容作为文本提供

linux - Linux 中的线程亲和性