无锁队列(MPSC/MPMC):Vyukov 环形队列与 slot 未就绪问题

C++ 进阶篇——智能指针、多线程与性能优化【移动语义、RAII、线程池实战,从会写到写好】 本文为C++进阶篇,聚焦内存管理、并发编程与性能优化三大核心。通过智能指针(unique/shared/weak_ptr)实现自动内存回收,避免裸指针陷阱;运用多线程四件套(thread、mutex、condition_variable、atomic)构建线程安全的生产者-消费者模型;最后以传参优化、容器选择和缓存友好设计提升代码效率。口诀总结:不再裸new、不再裸加锁、不再乱拷贝,助力写出高效、安全、可维护的现代C++代码。 阅读详情

无锁栈只有一个头指针要协调,无锁队列却有两个端点:头(出队)和尾(入队)。多生产者要抢尾巴,多消费者要抢头,中间还藏着一个新手最容易栽跟头的「slot 未就绪」问题。生产者用 fetch_add 抢到了槽位,但数据还没写完,消费者绝不能提前读。这篇用 Vyukov 有界环形队列(bounded MPMC queue)把这条线讲透,给出能真跑、TSan 干净的 MPSC 和 MPMC 两个版本,最后说清 MPSC 相对好写、MPMC 难在哪。

1. 引子:一个「读到了没写完的数据」的隐患

先看一个朴素的环形队列思路:用一个数组当缓冲区,生产者用 tail.fetch_add(1) 抢一个下标写数据,消费者用 head 读数据。这个思路错在哪里?

  朴素环形队列的「slot 未就绪」竞态(时间从上往下)

  生产者 P1                    生产者 P2                    消费者 C
  ──────────                  ──────────                  ──────────
  [1] tail.fetch_add(1)=0      [2] tail.fetch_add(1)=1
      抢到 slot 0                  抢到 slot 1
                                [3] buffer[1]=42(写完)
                                                           [4] 读 head=0,
                                                               buffer[0] 还是旧值/垃圾!
  [5] buffer[0]=7(P1 这时才写完)

  结果:消费者在 P1 写完之前就读了 slot 0,读到的是脏数据。
  根因:fetch_add 只「分配」了槽位,不保证「数据已经写完」。

tail.fetch_add(1) 是原子的,但它只能保证「每个生产者拿到不同的下标」,保证不了「拿到下标 = 数据已经可见」。两个生产者几乎同时抢槽,快的那个(P2)先写完,慢的那个(P1)还没写,消费者却可能已经读到了慢的槽位。这正是无锁队列区别于无锁栈的核心难点。

2. 解法:sequence 数组,给每个槽位一个「轮次号」

Vyukov 有界队列的解法很漂亮:给每个槽位配一个**sequence(轮次号)**原子量,用它来编码「这个槽位现在处于第几轮、是否可写/可读」。

  Vyukov 有界环形队列(capacity = 4,mask = 3)

  buffer:  [ B0 ][ B1 ][ B2 ][ B3 ]          ← 真正存数据的数组
  seq:     [ S0 ][ S1 ][ S2 ][ S3 ]          ← 每个槽位的轮次号(原子)

  初始化:seq[i] = i(第 i 个槽位第 0 轮可写)

  入队(生产者):
    pos = tail.fetch_add(1)               ← 抢一个位置
    自旋等待 seq[pos & mask] == pos        ← 等到「这一轮轮到我了」
    写 buffer[pos & mask] = value
    seq[pos & mask] = pos + 1             ← 发布:下一轮(pos+1)可读

  出队(消费者):
    pos = head.fetch_add(1)               ← 抢一个位置
    自旋等待 seq[pos & mask] == pos + 1    ← 等到「数据已经写完」
    读 value = buffer[pos & mask]
    seq[pos & mask] = pos + capacity      ← 归还槽位:下一轮(pos+cap)可写

  「满」判定:入队时 seq < pos(槽位还被上一轮占着)
  「空」判定:出队时 seq < pos + 1(还没轮到这一轮)

关键就在那个 seq:入队者等 seq == pos,出队者等 seq == pos + 1。一个槽位被写完,seq 从 pos 变成 pos+1,等于向消费者「发布」了数据;消费者读完,把 seq 改成 pos+capacity,等于「归还」槽位让下一轮生产者复用。生产者和消费者之间靠 seq 上的 acquire/release 完成同步,而不是靠 fetch_add 的先后顺序。

官方文档:std::memory_order(acquire/release 配对) · std::atomic::fetch_add

3. 完整实现:Vyukov 有界 MPMC 队列

下面是完整的实现,capacity 必须是 2 的幂(这样 & mask 就能取模)。buffer_ 存数据,seq_ 存轮次号,tail_/head_ 是两个单调递增的原子计数:

// 片段:Vyukov 有界 MPMC 队列(capacity 必须是 2 的幂)
#include <atomic>
#include <cstddef>
#include <vector>

template <typename T>
class BoundedMpmcQueue {
public:
    explicit BoundedMpmcQueue(std::size_t cap)
        : capacity_(cap), mask_(cap - 1), buffer_(cap), seq_(cap) {
        for (std::size_t i = 0; i < cap; ++i)
            seq_[i].store(i, std::memory_order_relaxed);   // 第 0 轮可写
    }

    bool try_enqueue(const T& value) {
        std::size_t pos = tail_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq)
                                - static_cast<std::ptrdiff_t>(pos);
            if (diff == 0) {                                // 轮到这一轮写
                if (tail_.compare_exchange_weak(pos, pos + 1,
                                                std::memory_order_relaxed)) {
                    buffer_[pos & mask_] = value;
                    seq_[pos & mask_].store(pos + 1, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) {
                return false;                               // 队列满
            } else {
                pos = tail_.load(std::memory_order_relaxed); // 别的生产者抢先,重读
            }
        }
    }

    bool try_dequeue(T& out) {
        std::size_t pos = head_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq)
                                - static_cast<std::ptrdiff_t>(pos + 1);
            if (diff == 0) {                                // 数据已经写完
                if (head_.compare_exchange_weak(pos, pos + 1,
                                                std::memory_order_relaxed)) {
                    out = buffer_[pos & mask_];
                    seq_[pos & mask_].store(pos + capacity_,
                                            std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) {
                return false;                               // 队列空
            } else {
                pos = head_.load(std::memory_order_relaxed);
            }
        }
    }

private:
    std::size_t capacity_;
    std::size_t mask_;
    std::vector<T> buffer_;
    std::vector<std::atomic<std::size_t>> seq_;
    std::atomic<std::size_t> tail_{0};
    std::atomic<std::size_t> head_{0};
};

官方文档:std::atomic(std::vector<std::atomic<T>> 的默认构造可用)

4. MPSC:多生产者单消费者

MPSC(multi-producer single-consumer)只有生产端有竞争,消费端是单线程独占 head。下面 4 个生产者各塞 4 个互不重复的值,1 个消费者全部取出,验证总和确定:

// mpsc.cpp — 编译: g++ -std=c++17 -Wall -O2 -pthread mpsc.cpp -o mpsc
#include <atomic>
#include <cstddef>
#include <cstdio>
#include <thread>
#include <vector>

template <typename T>
class BoundedMpmcQueue {
public:
    explicit BoundedMpmcQueue(std::size_t cap)
        : capacity_(cap), mask_(cap - 1), buffer_(cap), seq_(cap) {
        for (std::size_t i = 0; i < cap; ++i) seq_[i].store(i, std::memory_order_relaxed);
    }
    bool try_enqueue(const T& value) {
        std::size_t pos = tail_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq) - static_cast<std::ptrdiff_t>(pos);
            if (diff == 0) {
                if (tail_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    buffer_[pos & mask_] = value;
                    seq_[pos & mask_].store(pos + 1, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) return false;
            else pos = tail_.load(std::memory_order_relaxed);
        }
    }
    bool try_dequeue(T& out) {
        std::size_t pos = head_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq) - static_cast<std::ptrdiff_t>(pos + 1);
            if (diff == 0) {
                if (head_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    out = buffer_[pos & mask_];
                    seq_[pos & mask_].store(pos + capacity_, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) return false;
            else pos = head_.load(std::memory_order_relaxed);
        }
    }
private:
    std::size_t capacity_, mask_;
    std::vector<T> buffer_;
    std::vector<std::atomic<std::size_t>> seq_;
    std::atomic<std::size_t> tail_{0}, head_{0};
};

int main() {
    constexpr int kProducers = 4;
    constexpr int kPerProducer = 4;
    constexpr int kTotal = kProducers * kPerProducer;

    BoundedMpmcQueue<int> q(16);              // 16 个槽位

    std::vector<std::thread> producers;
    for (int t = 0; t < kProducers; ++t) {
        producers.emplace_back([&, t] {
            for (int i = 0; i < kPerProducer; ++i) {
                while (!q.try_enqueue(t * kPerProducer + i)) { }
            }
        });
    }
    for (auto& t : producers) t.join();

    long long sum = 0;                        // 单个消费者,独占 head
    for (int i = 0; i < kTotal; ++i) {
        int v = 0;
        while (!q.try_dequeue(v)) { }
        sum += v;
    }

    std::printf("MPSC 出队 %d 个,值之和 = %lld(期望 %d)\n",
                kTotal, sum, kTotal * (kTotal - 1) / 2);
}
MPSC 出队 16 个,值之和 = 120(期望 120)

120 = 0+1+…+15。注意消费者是单线程,try_dequeue 固定调用 16 次就能取完。这正是 MPSC 好写的地方:消费端没有竞争,head 只有一个人动。

5. MPMC:多生产者多消费者,难在哪

MPMC(multi-producer multi-consumer)两端都有竞争:生产者抢 tail_,消费者抢 head_。难点在于两端都要做完整的 sequence 协议,任何一个端点少一步都会出错。下面 4 生产者 + 4 消费者,用一个原子 remaining 计数保证恰好出队 16 次:

// mpmc.cpp — 编译: g++ -std=c++17 -Wall -O2 -pthread mpmc.cpp -o mpmc
#include <atomic>
#include <cstddef>
#include <cstdio>
#include <thread>
#include <vector>

template <typename T>
class BoundedMpmcQueue {
public:
    explicit BoundedMpmcQueue(std::size_t cap)
        : capacity_(cap), mask_(cap - 1), buffer_(cap), seq_(cap) {
        for (std::size_t i = 0; i < cap; ++i) seq_[i].store(i, std::memory_order_relaxed);
    }
    bool try_enqueue(const T& value) {
        std::size_t pos = tail_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq) - static_cast<std::ptrdiff_t>(pos);
            if (diff == 0) {
                if (tail_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    buffer_[pos & mask_] = value;
                    seq_[pos & mask_].store(pos + 1, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) return false;
            else pos = tail_.load(std::memory_order_relaxed);
        }
    }
    bool try_dequeue(T& out) {
        std::size_t pos = head_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq) - static_cast<std::ptrdiff_t>(pos + 1);
            if (diff == 0) {
                if (head_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    out = buffer_[pos & mask_];
                    seq_[pos & mask_].store(pos + capacity_, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) return false;
            else pos = head_.load(std::memory_order_relaxed);
        }
    }
private:
    std::size_t capacity_, mask_;
    std::vector<T> buffer_;
    std::vector<std::atomic<std::size_t>> seq_;
    std::atomic<std::size_t> tail_{0}, head_{0};
};

int main() {
    constexpr int kProducers = 4;
    constexpr int kPerProducer = 4;
    constexpr int kConsumers = 4;
    constexpr int kTotal = kProducers * kPerProducer;

    BoundedMpmcQueue<int> q(16);

    std::vector<std::thread> producers;
    for (int t = 0; t < kProducers; ++t) {
        producers.emplace_back([&, t] {
            for (int i = 0; i < kPerProducer; ++i) {
                while (!q.try_enqueue(t * kPerProducer + i)) { }
            }
        });
    }
    for (auto& t : producers) t.join();

    std::atomic<int> remaining{kTotal};     // 恰好触发 kTotal 次出队
    std::atomic<long long> sum{0};
    std::atomic<int> count{0};
    std::vector<std::thread> consumers;
    for (int t = 0; t < kConsumers; ++t) {
        consumers.emplace_back([&] {
            for (;;) {
                if (remaining.fetch_sub(1, std::memory_order_relaxed) <= 0) break;
                int v = 0;
                while (!q.try_dequeue(v)) { }
                sum.fetch_add(v, std::memory_order_relaxed);
                count.fetch_add(1, std::memory_order_relaxed);
            }
        });
    }
    for (auto& t : consumers) t.join();

    std::printf("MPMC 出队总数 = %d(期望 %d)\n", count.load(), kTotal);
    std::printf("值之和       = %lld(期望 %d)\n", sum.load(), kTotal * (kTotal - 1) / 2);
}
MPMC 出队总数 = 16(期望 16)
值之和       = 120(期望 120)

这段用 clang 19 配 -fsanitize=thread 真跑过,stderr 为空、没有 data race。同步全部落在 seq_ 的 acquire/release 上,没有任何对共享数据的裸读写竞争。

三种队列的难度对比:

类型生产端消费端关键竞争难度
SPSC单生产者单消费者无(头尾各归一方)最低,一个原子都不用
MPSC多生产者单消费者生产者抢 tail_中,消费端可简化
MPMC多生产者多消费者头尾都要抢高,两端都要 sequence 协议

MPSC 相对好写的本质:消费端是单线程,head 可以用普通 size_t,只需要处理生产端的 slot 未就绪;而 MPMC 的 head 也必须原子化,消费者之间还要靠 sequence 协议互相让路,出错面翻倍。

6. 完整示例:满/空判定与环形复用

前面两个例子都是「正好装满、一次取完」,没展示有界队列的满/空边界。下面单线程把 capacity=4 的队列填满再取空,验证 try_enqueue/try_dequeue 的 bool 返回值正确标记满/空,并演示环形复用(取空后再入队仍能成功):

// ring.cpp — 编译: g++ -std=c++17 -Wall -O2 ring.cpp -o ring
#include <atomic>
#include <cstddef>
#include <cstdio>
#include <vector>

template <typename T>
class BoundedMpmcQueue {
public:
    explicit BoundedMpmcQueue(std::size_t cap)
        : capacity_(cap), mask_(cap - 1), buffer_(cap), seq_(cap) {
        for (std::size_t i = 0; i < cap; ++i) seq_[i].store(i, std::memory_order_relaxed);
    }
    bool try_enqueue(const T& value) {
        std::size_t pos = tail_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq) - static_cast<std::ptrdiff_t>(pos);
            if (diff == 0) {
                if (tail_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    buffer_[pos & mask_] = value;
                    seq_[pos & mask_].store(pos + 1, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) return false;
            else pos = tail_.load(std::memory_order_relaxed);
        }
    }
    bool try_dequeue(T& out) {
        std::size_t pos = head_.load(std::memory_order_relaxed);
        for (;;) {
            std::size_t seq = seq_[pos & mask_].load(std::memory_order_acquire);
            std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(seq) - static_cast<std::ptrdiff_t>(pos + 1);
            if (diff == 0) {
                if (head_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed)) {
                    out = buffer_[pos & mask_];
                    seq_[pos & mask_].store(pos + capacity_, std::memory_order_release);
                    return true;
                }
            } else if (diff < 0) return false;
            else pos = head_.load(std::memory_order_relaxed);
        }
    }
private:
    std::size_t capacity_, mask_;
    std::vector<T> buffer_;
    std::vector<std::atomic<std::size_t>> seq_;
    std::atomic<std::size_t> tail_{0}, head_{0};
};

int main() {
    BoundedMpmcQueue<int> q(4);               // 只有 4 个槽位

    int ok = 0;
    for (int i = 0; i < 5; ++i)               // 尝试塞 5 个,第 5 个应失败(满)
        if (q.try_enqueue(i * 10)) ++ok;
    std::printf("入队成功 %d 次(期望 4,第 5 次因满失败)\n", ok);

    int v = 0, cnt = 0;
    long long sum = 0;
    while (q.try_dequeue(v)) {                // 取到空为止
        sum += v;
        ++cnt;
    }
    std::printf("出队 %d 个,值之和 = %lld(期望 %d 个,和 60)\n",
                cnt, sum, 4);

    std::printf("再入队一个 %s\n", q.try_enqueue(99) ? "成功(环形复用)" : "失败");
}
入队成功 4 次(期望 4,第 5 次因满失败)
出队 4 个,值之和 = 60(期望 4 个,和 60)
再入队一个 成功(环形复用)

60 = 0+10+20+30,第 5 次入队被「满」判定拦下;取空后(try_dequeue 返回 false 时停止)再入队又成功了——槽位被 seq[pos + capacity] 归还,环形缓冲区真正转了起来。

最后照例提醒:这里只演示了固定容量、元素可平凡复制的场景。真实的无锁队列还要处理「动态扩容」「内存回收」「等待而非自旋」这些需求,而且正确性极难肉眼验证——生产级请直接用 moodycamel::ConcurrentQueue(concurrentqueue)、Boost.Lockfree 的 spsc_queue/queue、或 liblfds,它们经过海量测试和模型检查,自己手写只适合学习和理解原理。

7. 延伸阅读

本知识库内的相关篇目:

8. 一句话总结

无锁队列比栈难在有两个端点,且 fetch_add 只「分配槽位」不保证「数据已写完」。Vyukov 有界队列用 sequence 数组给每个槽位记轮次号,入队等 seq==pos、出队等 seq==pos+1,靠 acquire/release 完成生产消费同步;MPSC 消费端单线程、只有生产端抢 tail_ 所以相对好写,MPMC 头尾都要抢、两端都要完整 sequence 协议所以难;无论哪种,正确性都极难手写验证,生产级用 concurrentqueue / Boost.Lockfree 这类现成库。

无耳科技 Solon v3.0.8 发布,Java 企业级应用开发框架 Solon 是新一代,Java 企业级应用开发框架。是杭州无耳科技有限公司的“根级”开源项目(最近“杭州六小龙”很火啊,我们也是杭州的哦)。从零开始构建(No Spring、No Java-EE、No Servlet),有灵活的接口规范与开放生态。采用对商用友好的 Apache 2.0 开源协议。 阅读详情

相关推荐

【零基础入门哈希签名架构,从Lamport到SPHINCS+】第二集 哈希签名树 Merkle Tree Authentication

在第一集中,我们一直忽略了一个问题。设想一下,假如有一个Hacker(黑客),在截获到Sender发送给Receiver的公钥之后,把这个公钥改成Hacker自己的公钥,然后再发给Receiver,那么Receiver就会误认为这是Sender的公钥。所以我们之前所说放方案,其安全性都需要建立在一个前提下:Receiver需要确认公钥确实是Sender发出的。

m0_50572808的博客 2451

闪控窗性能优化与多实例资源治理【鸿蒙心迹】

本文是闪控窗系列第三篇,聚焦**多实例场景下的资源治理**。针对用户高频开关、多任务切换导致闪控窗实例越攒越多、内存与绘制开销失控的问题,给出"实例有上限、绘制会节流、销毁能统一"的完整方案:用全局单例 `FloatMgr` 统筹实例,设硬上限(建议 3 个)并淘汰最旧;用空闲实例池复用避免频繁 create/destroy;用 `Animator` 把静止态刷新压到 1fps 甚至停更;页面销毁与退后台统一 `releaseAll`,并配合 5 分钟超时回收。

wdracky的专栏 276

手把手教你搞定爱思唯尔LaTeX投稿:从模板到PDF生成的全流程避坑指南

本文详细介绍了使用LaTeX投稿爱思唯尔期刊的全流程避坑指南,包括模板选择、文件结构规范、投稿系统文件上传策略、PDF生成与检查等关键步骤。特别强调了避免常见错误如宏包冲突、图片丢失和参考文献问题,并提供了投稿后的应急方案和高阶自动化校验脚本,帮助研究者顺利完成投稿。

weixin_42525289的博客 506

〖物流终端实战 05〗纯血鸿蒙 ArkTS 高阶性能优化:长列表渲染、组件复用与内存泄漏排查

本文深度剖析鸿蒙ArkTS高阶性能优化,直击长列表卡顿与内存泄漏痛点。针对渲染,指出ForEach全量构建的缺陷,强调采用LazyForEach(V2推荐Repeat)配合增量通知及稳定业务Key;针对组件复用,明确@Reusable仅复用实例不重置状态,必须在aboutToReuse中手动清理UI状态;针对内存泄漏,总结全局监听未注销、定时器未清理及长生命周期对象持有页面引用三大元凶,并指导通过DevEco Profiler的Heap快照排查引用链。文章强调优化需遵循“先定位后改代码”原则,助力开发者打造

Eyf • 半字节的博客 620

MTK metadata中AF模块的移除与配置调整

本文详细介绍了在MTK平台Camera开发中如何移除metadata中的AF(自动对焦)模块。通过系统清理MTK_CONTROL_AF_REGIONS、MTK_CONTROL_MAX_REGIONS等关键配置项,并调整对焦模式及硬件特性声明,确保定制化设备在禁用自动对焦功能后的稳定运行。

q1w3e5r7t9y的博客 570

【理论&实战篇】大模型RAG技术详解(一)-基础篇

RAG的初衷是为了增强大模型的事实性、时效性,减少幻觉,引入专业知识优化生成回复,是一种结合了信息检索技术与语言生成模型的人工智能技术。通过,并将其作为提示(Prompt)输入给大模型(LLM),来。

qq_41375680的博客 373

AI 应用开发工程师技术实践:大模型接入、通用组件封装与性能优化

本文提出AI应用从PoC到生产的核心挑战:模型调用的持续可用、可控与可优化。通过构建统一LLM网关实现多供应商路由与故障转移,封装对话、工具调用与RAG组件以解耦业务逻辑,结合前缀缓存、批处理与流式背压治理等性能优化手段,形成“接通→接稳→接快”的工程路径,最终以工程化能力决定AI应用的生产可用性。

2301_79189962的博客 516

Spring AI + RAG 构建电商智能客服:从 PDF 文档解析到精准问答的全链路实战

核心框架:向量数据库:- PostgreSQL + pgvector (生产推荐)- Redis Stack (轻量级方案)- ChromaDB (快速原型)文档解析:- Apache PDFBox: PDF 文本提取- Spring AI Document Readers: 结构化解析大模型:- 阿里云百炼 (DashScope)- 本地部署:Qwen/ChatGLM/*** 电商领域实体*/@Data@Builder@ToString/*** 商品类型。

一起修行,不孤单。这里有编程语言、开发工具、学习方法论和踩坑笔记。用简单的话说复杂的事,陪你从小白到进阶。周更干货,欢迎一起成长! 379

RISC-V 基础软件的工程化与性能优化实践

从软件生态现状、优化方法到在龙蜥社区、如意社区等的开源贡献,这些探索展现了基础软件连接硬件能力与业务价值的作用,也为 RISC-V 生态共建提供了实践参考。

weixin_60347558的博客 356

【雷达图】R语言绘制雷达图(ggradar),NBA季后赛数据为例

一起用R语言给nba球星画球技雷达图吧!

池塘春草梦的博客 6252

基于Active-HDL的HDL设计录入与仿真

作者:毛茏玮 / Saint 掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a 微博:https://weibo.com/5458277467/profile?topnav=1&amp;wvr=6&amp;is_all=1 GitHub:github.com/saint-000 优快云: https://me.youkuaiyun.com/qq_4053...

一个会写代码的烤鸭 7780

ABAP 程序性能优化总结

排序要按照字段的读取顺序进行排序!否则二分法BINARY SEARCH会读取失败。推荐「在 DB 做 LEFT JOIN」,而不是读出來再 READ 匹配。尽量减少第一步结果集的行数,这样可以大量减少后续连接大表时的耗时。减少辅助表的行数,也就是需要读取数据的内表的行数。程序性能优化、效率优化、速度优化。

weixin_44686958的博客 159

如何用DeepSeek写年底工作总结?——从入门到精通的全流程指南

AI助手DeepSeek如何高效撰写工作总结 摘要:本文介绍如何利用DeepSeek智能AI助手高效完成工作总结写作。工作总结具有结构固定、语言规范、内容数据化三大特性,而DeepSeek的结构化生成、风格适配、数据整合和迭代优化四大核心能力与之完美匹配。文章提出"准备-搭架-填充-优化"四步工作法:1)精准输入岗位、时间范围等核心信息;2)先搭建逻辑清晰的提纲框架;3)分模块填充具体内容,突出数据与案例;4)通过风格润色和细节补充进行优化。通过合理运用多模态材料处理能力,DeepSee

通俗易懂学IT 285

医院医学学术交流语音处理性能优化实践:单机CPU瓶颈、并发控制与硬件边界分析

针对医院医学学术交流中的语音转写与多语种翻译需求,分析单机CPU处理瓶颈,通过任务队列、并发控制和资源监控优化系统吞吐量与运行稳定性。

HySpark的博客 335

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

.NET 性能优化:先找瓶颈,再谈技巧

NET 性能优化没有“一招鲜”。Span<T>ValueTaskstackalloc这些工具在特定场景下确实很强,但它们解决的是微观层面的分配问题。如果你的应用瓶颈在数据库查询或者线程池阻塞,用再多Span<T>也救不回来。先测量,找到热路径,再针对性修改,改完再测量验证。跳过第一步的优化,大概率是在给编译器添乱。

qq_41019429的博客 353

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

美团发布新一代大模型LongCat-2.5,面向长程智能体任务的原生多模态模型

从LongCat-2.5的技术路线看,模型升级重点已经从单纯扩大参数规模,转向视觉统一建模、长程Agent训练、训推一致性和国产算力适配等系统工程。尤其是在复杂代码库开发、3D页面生成和多轮工具调用等任务中,模型能否持续执行、验证结果并根据失败轨迹修正策略,将成为衡量Agent能力的重要指标。LongCat-2.5把原生视觉理解与长程Agent训练结合起来,也意味着多模态模型正在从“看懂内容”进一步走向“完成任务”。

Enweitech Software Works 210

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

用用户认证系统一次讲清 Serverless 性能优化实战

在现代 Web 应用开发中,用户认证与权限管理是不可或缺的模块。随着业务规模的扩大,传统的服务器部署模式逐渐暴露出响应延迟、资源浪费等问题。Serverless 架构因其按需付费、自动扩展的特性,成为许多开发者的首选。然而,在实际使用过程中,开发者往往忽视了对 Serverless 函数性能的优化,导致用户体验下降、成本增加。本文将围绕一个真实的用户认证场景展开,通过基准测试定位性能瓶颈,并进行一系列优化措施,最终实现响应时间降低 60% 的显著提升。

后台模板 182

从内核源码出发:卡顿/冻屏/黑屏问题的日志关键字速查与性能优化实战指南

第一步:dmesg 大口径过滤第二步:命中后定位源码用日志原字符串 grep 源码树 → 落到 file:line → 看触发条件第三步:顺栈分析调用栈最底层函数 → grep 调用者 → 检查锁/中断/睡眠上下文是否合法第四步:量化验证(日志无命中时)ftrace/perf 抓取 → 量化 runnable 延迟 / 回收耗时 / VSync 间隔第五步:优化修复按第六节子系统优化清单逐项验证核心心法:关键字记忆是辅助,"拿串搜树"才是本能。每一个pr_errpr_warn。

weixin_43228946的博客 202

Python 进阶实战:装饰器、生成器、描述符与 GIL 并发一篇打通【上下文管理器、元类、asyncio 异步、性能优化】

本文深入解析Python进阶核心机制:装饰器的闭包本质、生成器的惰性协程特性、描述符的属性控制协议及GIL下的并发真相。通过可运行代码与面试追问点,揭示“会写”与“懂”的分水岭,助开发者突破熟练层,真正掌握语言底层逻辑,实现编程能力跃迁。

王大师企业官方博客 990

Coding Agent 推理性能优化:Decode、KV Cache 与多 Session 调度

单用户 Decode 提升 2.8 倍,单实例吞吐提升 5.6 倍。

2501_92842654的博客 215

开源多维表格SmartTable v1.6.10 —— 用户名登录、数据评论及@人、表单分享二维码、性能优化、部署及升级优化

这版主要干了这几件:💬。

Binac二维思索 256

Chrome浏览器视频播放性能优化配置完整指南

本文从网络排查、硬件加速验证、核心参数配置、缓存清理、扩展管理等维度,手把手带你解决Chrome浏览器视频卡顿、跳帧、CPU占用过高的问题,快速实现4K视频流畅硬解码。

the3clipse的博客 337

上架冷启动性能优化与启动耗时治理【鸿蒙心迹】

本文针对鸿蒙应用冷启动慢、用户“秒开变转圈”的痛点,提出“阻塞/并行/延迟”三档任务拆解法,结合hiTraceMeter打点量化耗时,将首屏可交互时间从2.8s降至0.9s(降68%)。提供可复用代码骨架、任务分档清单与实战清单,强调首屏只做必需事、非关键操作延迟执行,并通过限流、骨架屏等策略优化体验。核心建议:启动路要“修干净”,发版前真机测试卡点≤1s,确保上架后口碑不被差评拖累。

wdracky的专栏 239

Kimi K3.1 Pro来了:基于CANN的Day0推理支持与性能优化

10月24日,Moonshot AI发布Kimi K3.1 Pro,并于11月4日正式开源。CANN完成Kimi K3.1 Pro的Day0支持与优化,助力开发者在昇腾平台上快速部署与体验。

m0_71340392的博客 332

VisionPro高级案例——位置检测

基于VisionPro高级脚本,实现多色药片自动分拣与计数:通过CogColorExtractor按颜色分组提取掩膜,动态输入CogBlobTool进行连通域分析,精准定位每粒药片质心并标注坐标;在界面上以红十字标记中心点,上方汇总显示各颜色药片数量。代码支持批量处理多张图片,具备高可扩展性,适用于多色物料分类场景。

weixin_66547608的博客 438

2026-11-04-RemoteControl-性能优化-对比评测-踩坑排错

零依赖 Node.js Web 远控:15fps 档实测仅 2.7 帧,单帧 132KB,10 客户端出口 21.7Mbps。本文拆开截屏到渲染全链路,算清帧率与带宽账,给出 9 条协议性能坑及长驻进程、差分、背压的优化清单。

Dai_sir_man的博客 180
上一篇: 类型列表与编译期算法:手写 typelist 的 map/filter
下一篇: 无锁栈:Treiber 栈的 CAS、ABA 问题与 tagged pointer
无名猿
博客等级 码龄10年 61粉丝 184原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值