AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / user-15649230

Ahmed AEK's questions

Martin Hope
Ahmed AEK
Asked: 2025-04-27 05:06:58 +0800 CST

在没有同步的情况下读取原子指针是否安全?

  • 7

假设一个原子指针永远不会为 nullptr ,那么在没有同步的情况下读取它是否安全?就像下面的代码一样,假设有两个线程writer同时运行reader。

std::atomic<int>* g_atomic = new std::atomic<int>{};

void writer()
{
    for (int i = 0; i < 101; i++)
    {
        auto* new_atomic = new std::atomic<int>{i};
        std::atomic_thread_fence(std::memory_order_seq_cst); // memory barrier.
        g_atomic = new_atomic; // ignore the memory leak
    }
}

void reader()
{
    auto value = g_atomic->load();
    while (value < 100)
    {
        assert(value >= 0 && value <= 100);
        value = g_atomic->load();
    }
}

我所说的安全是指,我将始终读取从 0 到 100 的值,我不会读取无效指针或在初始化之前读取指向的对象。

我的直觉告诉我这是安全的,因为

  1. 在所有架构上,指针都是以原子方式读取或写入的。
  2. 指向的值是原子读取的,必须从 RAM 中获取,并且写入之前的内存屏障保证 RAM 始终正确。

那么,这安全吗?也许只适用于所有常见的架构?

c++
  • 2 个回答
  • 86 Views
Martin Hope
Ahmed AEK
Asked: 2025-04-25 15:25:20 +0800 CST

比较两个指向同一容器的跨度是否定义明确?

  • 17

指向同一内存的两个 s 的比较begin()和迭代器是否定义明确?end()std::span

#include <span>
#include <cassert>

int main() {
    int arr[5]{1,2,3,4,5};
    std::span s1{arr};
    std::span s2{arr};
    assert(s1.begin() == s2.begin());
    assert(s1.end() == s2.end());
    assert(s1.begin() + s1.size() == s2.end());
}

到目前为止,所有断言都传递了所有实现std::span,但是我是否遗漏了什么,从而导致出现错误,例如 UB?

就上下文而言,如果您有一个试图用跨度隐藏其内部的类,则可能会出现这种情况。

class some_class
{
public:
  std::span<int> vec_view() { return vec; }
private:
  std::vector<int> vec;
};

int main() {
    some_class c;
    std::for_each(c.vec_view().begin(), c.vec_view().end(), [](auto&){});
}

这与当一个跨度是另一个跨度的子跨度时,C++ 是否允许在 std::span::iterators 之间进行比较?有关,但这个问题与 无关std::subspan,而且该std::span版本还通过了 MSVC 断言,与 的版本不同std::subspan。

c++
  • 2 个回答
  • 489 Views
Martin Hope
Ahmed AEK
Asked: 2025-03-09 11:13:31 +0800 CST

为什么虚拟+间接函数调用比虚拟或间接函数调用更快?

  • 5

我试图测量间接的成本,因为将间接堆叠在一起会降低我试图测量的性能。

  1. 直接函数调用(直接调用同一 DLL 中的函数,可能内联)
  2. 间接函数调用(通过指针调用另一个 DLL 中的函数,而不是内联)
  3. 虚函数调用。
  4. 虚函数调用,然后是间接函数调用(保存函数指针的多态类)。

为了防止编译器进行任何内联,我将其拆分exe为dll

可执行代码

#include <limits.h>
#include <vector>
#include <chrono>
#include <iostream>
#include <span>

static int foo(int a, std::vector<int>& v) {
    v.push_back(a);
    if (v.size() > 2)
    {
        v.clear();
    }
    return a;
}

struct IFooable
{
    virtual int foo(int a) = 0;
};

__declspec(dllimport) int foo2(int a, std::vector<int>& v);

__declspec(dllimport) int direct_version(std::vector<int>& v);

__declspec(dllimport) int indirect_version(int (*fn)(int, void*), void* p);

__declspec(dllimport) int indirect_Interface(IFooable& f);

struct MyFoo final: public IFooable
{
    int foo(int a) override
    {
        return ::foo(a, v);
    }
    MyFoo(std::vector<int>& v) : v{ v } {}
    std::vector<int>& v;
};

struct MyFoo2 final : public IFooable
{
    using functype = int (*)(int, std::vector<int>&);
    int foo(int a) override
    {
        return f(a, v);
    }
    MyFoo2(std::vector<int>& v, functype f) : v{ v }, f{ f } {}
    std::vector<int>& v;
    functype f;
};

int main(int argc, char* argv[]) {
    std::vector<int> v;
    for (int i = 0; i < 20; i++)
    {
        foo(i, v);
    }

    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();

    direct_version(v);
    std::chrono::steady_clock::time_point end1 = std::chrono::steady_clock::now();

    indirect_version([](int a, void* p) {return foo(a, *reinterpret_cast<std::vector<int>*>(p)); }, reinterpret_cast<void*>(&v));
    std::chrono::steady_clock::time_point end2 = std::chrono::steady_clock::now();

    MyFoo ff{ v };
    indirect_Interface(ff);
    std::chrono::steady_clock::time_point end3 = std::chrono::steady_clock::now();
    MyFoo2 ff2{ v, foo2 };
    indirect_Interface(ff2);
    std::chrono::steady_clock::time_point end4 = std::chrono::steady_clock::now();

    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end1 - begin).count() << "[ms] " << "Direct" << std::endl;
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end2 - end1).count() << "[ms] " << "Indirect" << std::endl;
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end3 - end2).count() << "[ms] " << "Virtual" << std::endl;
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end4 - end3).count() << "[ms] " << "Virtual + Indirect" << std::endl;

    double micros_count = std::chrono::duration_cast<std::chrono::milliseconds>(end2 - end1).count();
    double iterations = INT_MAX;
    std::cout << "nanoseconds per iteration = " << (micros_count / iterations) * 1000000 << '\n';
    
    return 0;
}

dll代码

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <vector>
#include <span>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

struct IFooable
{
    virtual int foo(int a) = 0;
};

static int foo(int a, std::vector<int>& v) {
    v.push_back(a);
    if (v.size() > 2)
    {
        v.clear();
    }
    return a;
}

__declspec(dllexport) int foo2(int a, std::vector<int>& v) {
    v.push_back(a);
    if (v.size() > 2)
    {
        v.clear();
    }
    return a;
}

__declspec(dllexport) int direct_version(std::vector<int>& v) {
    int i, b = 0;
    for (i = 0; i < INT_MAX; ++i) {
        b = foo(b, v);
    }
    return b;
}

__declspec(dllexport) int indirect_version(int (*fn)(int, void*), void* p) {
    int i, b = 0;

    for (i = 0; i < INT_MAX; ++i) {
        b = fn(b, p);
    }

    return b;
}

__declspec(dllexport) int indirect_Interface(IFooable& f) {
    int i, b = 0;

    for (i = 0; i < INT_MAX; ++i) {
        b = f.foo(b);
    }

    return b;
}

它们是在发布模式下编译的,因此/O2,基准测试的构建方式可以避免任何缓存未命中,并且可能允许 CPU 预测所有函数将指向的位置,因为我对分支预测错误或缓存未命中的成本不感兴趣,而且该函数并不简单,但足够小,并且我已经检查了汇编代码,没有任何内容按预期内联。

结果:

3058[ms] Direct
8279[ms] Indirect
9109[ms] Virtual
7340[ms] Virtual + Indirect
nanoseconds per iteration = 3.85521

正如预期的那样,虚拟函数比间接调用稍微慢一些,但是虚拟+间接调用比任何一个都快......这没有意义。

我的问题是,为什么虚拟函数 + 间接寻址比单独使用任何一个都快?我是否应该实际预期这种级联间接寻址的成本会增加?

使用具有虚函数的 C++ API 包装另一个 DLL 中的 C API 的情况并不少见,因此这个基准尽可能接近现实世界。

交换调用的顺序不会改变任何东西,它们也足够长,以至于任何诸如 CPU 加速和过热以及调度程序之类的因素都不会影响基准。


编辑:看起来缓冲区溢出检查稍微歪曲了结果,这是现在没有那些额外检查的较新的结果。

3051[ms] Direct
6182[ms] Indirect
8002[ms] Virtual
7616[ms] Virtual + Indirect
nanoseconds per iteration = 2.87872

虚拟+间接仍然比仅虚拟更快,这非常奇怪。

c++
  • 1 个回答
  • 75 Views
Martin Hope
Ahmed AEK
Asked: 2024-03-02 05:25:53 +0800 CST

对象销毁后清理非拥有指针

  • 5

我通常会发现这个片段潜入我的代码中,其中两个互不拥有的对象需要有一个指向彼此的指针,并且一旦其中一个对象被销毁,这个指针就应该被“清理”,如下所示。

struct bar;
struct foo;

struct foo
{
    bar* bar_item = nullptr;
    ~foo();
};

struct bar
{
    foo* foo_item = nullptr;
    ~bar()
    {
        if (foo_item)
        {
            foo_item->bar_item = nullptr;
        }
    }
};

foo::~foo()
{
    if (bar_item)
    {
        bar_item->foo_item = nullptr;
    }
}

int main()
{
    foo obj1;
    bar obj2;
    obj1.bar_item = &obj2;
    obj2.foo_item = &obj1;
}

这里的问题是我必须

  1. 删除复制构造函数/赋值并实现移动构造函数/赋值(大量样板,预计每个类至少有 5 个成员)
  2. 将所有对象转换为shared_ptr,并且对象之间将具有weak_ptr。(也许并不总是可行,因为我可能不拥有对象的生命周期,也迫使我使用堆)

有什么方法可以避免这种情况吗?简化吗?是否有针对特定成员的 RAII 包装器以避免实施复制/移动操作?

我曾经遇到的一个简单的例子是一个图形系统,我需要一条线来连接两个球体,该线应该有指向两个球体的指针,而球体应该有一个指向该线的指针,所有这些都属于“场景” ,但是询问有关它们的“场景”需要很长的查找,指针避免了这种查找。

c++
  • 1 个回答
  • 56 Views
Martin Hope
Ahmed AEK
Asked: 2023-10-08 18:03:47 +0800 CST

基于输入类型的默认函数参数

  • 5

我有一种情况,当输入类型指定了某些特征时,我希望更改默认函数参数,例如 pmr 对象具有分配器特征和get_allocator函数,那么我该怎么做?我希望有一个不涉及 SFINAE 的 c++20 解决方案,除非不可能,否则可能使用 if constexpr 或概念,我不想编写两个函数,除非不可能。

template<typename T>
void new_deleter(T* ptr)
{
    delete ptr;
}

template <typename T>
void pmr_deleter(T* ptr)
{
    ptr->get_allocator().delete_object(ptr);
}

template<typename T>
using deleter_t = void (*)(T*);

// use pmr_deleter by default when possible
template<typename T>
void delete_object(T* ptr, deleter_t<T> deleter = &new_deleter<T>)
{
    deleter(ptr);
}

编辑:感谢答案,我想出了以下模式,它可以很容易地适应概念和 if contexpr。

template<typename T>
deleter_t get_default_deleter() {return &new_deleter<T>;}

template<typename T>
deleter_t get_default_deleter() 
requires requires (T* ptr) {ptr->get_allocator();}
{
 return &pmr_deleter<T>;
}

template<typename T>
void delete_object(T* ptr, deleter_t<T> deleter = get_default_deleter<T>())
{
    deleter(ptr);
}
c++
  • 2 个回答
  • 106 Views

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve