Skynet实践之「Lua C 模块集成—优先级队列」

本文展示了 如何以 C 实现一个通用的“最小堆(Min-Heap)优先队列 并在 Skynet 中通过 Lua C 模块集成使用,从而获得更高的性能与通用性。


一、C 语言模块:cpriorityqueue.c

以下代码演示了一个最小堆的数据结构(以 runTime 作为关键字),并提供了pushpoppeek等接口。该模块以 Lua C API 形式导出函数,让 Lua 层可以通过 require "cpriorityqueue" 来使用。

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct {
    double runTime; // 以这个作为排序字段,更通用的话可以改成 sortkey
    int id;
    // 其他字段也可放在这里,如 priority, pointers to lua references, etc.
} PQItem;

typedef struct {
    PQItem *array;  // 堆存储数组
    int size;       // 当前元素数量
    int capacity;   // 数组容量
} PriorityQueue;

//---------------------------
// 堆操作函数 (C层的私有函数)
//---------------------------

// 上浮操作
static void pq_sift_up(PriorityQueue *pq, int idx) {
    while (idx>1) {
        int parent = idx/2;
        if (pq->array[parent].runTime <= pq->array[idx].runTime)
            break;
        // swap
        PQItem tmp = pq->array[parent];
        pq->array[parent] = pq->array[idx];
        pq->array[idx] = tmp;
        idx = parent;
    }
}

// 下沉操作
static void pq_sift_down(PriorityQueue *pq, int idx) {
    int n = pq->size;
    while (1) {
        int left = idx*2;
        int right= idx*2+1;
        int smallest = idx;
        if (left<=n && pq->array[left].runTime < pq->array[smallest].runTime) {
            smallest = left;
        }
        if (right<=n && pq->array[right].runTime < pq->array[smallest].runTime) {
            smallest = right;
        }
        if (smallest==idx) break;
        // swap
        PQItem tmp = pq->array[idx];
        pq->array[idx] = pq->array[smallest];
        pq->array[smallest] = tmp;
        idx = smallest;
    }
}

//---------------------------
// PriorityQueue接口
//---------------------------
static PriorityQueue* pq_create(int cap) {
    PriorityQueue* pq = (PriorityQueue*)malloc(sizeof(PriorityQueue));
    pq->array = (PQItem*)malloc(sizeof(PQItem)*(cap+1)); // 1-based index
    pq->size = 0;
    pq->capacity = cap;
    return pq;
}

static void pq_free(PriorityQueue *pq) {
    if (!pq) return;
    if (pq->array) {
        free(pq->array);
    }
    free(pq);
}

static int pq_push(PriorityQueue *pq, double runTime, int id) {
    if (pq->size >= pq->capacity) {
        // auto expand
        int newcap = pq->capacity*2;
        PQItem *newarr = (PQItem*)realloc(pq->array, sizeof(PQItem)*(newcap+1));
        if (!newarr) return 0; // fail
        pq->array = newarr;
        pq->capacity = newcap;
    }
    pq->size++;
    int idx = pq->size;
    pq->array[idx].runTime = runTime;
    pq->array[idx].id = id;
    pq_sift_up(pq, idx);
    return 1;
}

static int pq_pop(PriorityQueue *pq, double *outVal) {
    if (pq->size==0) {
        return 0; // empty
    }
    // root
    *outVal = pq->array[1].id;
    pq->array[1] = pq->array[pq->size];
    pq->size--;
    if (pq->size>0) {
        pq_sift_down(pq, 1);
    }
    return 1;
}

static int pq_peek(PriorityQueue *pq, double *outVal) {
    if (pq->size==0) {
        return 0;
    }
    *outVal = pq->array[1].id;
    return 1;
}

//---------------------------
// Lua 接口绑定
//---------------------------

static int l_pq_create(lua_State* L) {
    int cap = luaL_optinteger(L, 1, 16);
    PriorityQueue* pq = pq_create(cap);
    // userdatum
    PriorityQueue** ud = (PriorityQueue**)lua_newuserdata(L, sizeof(PriorityQueue*));
    *ud = pq;
    // 设置 metatable
    luaL_getmetatable(L, "CPriorityQueueMT");
    lua_setmetatable(L, -2);
    return 1;
}

static int l_pq_gc(lua_State* L) {
    PriorityQueue** ud = (PriorityQueue**)luaL_checkudata(L, 1, "CPriorityQueueMT");
    if (*ud) {
        pq_free(*ud);
        *ud = NULL;
    }
    return 0;
}

static PriorityQueue* checkQueue(lua_State* L) {
    PriorityQueue** ud = (PriorityQueue**)luaL_checkudata(L, 1, "CPriorityQueueMT");
    luaL_argcheck(L, ud!=NULL && *ud!=NULL, 1, "invalid cpriorityqueue");
    return *ud;
}

static int l_pq_push(lua_State* L) {
    PriorityQueue* pq = checkQueue(L);
    double val = luaL_checknumber(L, 2);
    double id = luaL_checknumber(L, 3);
    int ret = pq_push(pq, val, id);
    lua_pushboolean(L, ret);
    return 1;
}

static int l_pq_pop(lua_State* L) {
    PriorityQueue* pq = checkQueue(L);
    double outVal;
    int ret = pq_pop(pq, &outVal);
    if (!ret) {
        return 0; // return nil
    }
    lua_pushnumber(L, outVal);
    return 1;
}

static int l_pq_peek(lua_State* L) {
    PriorityQueue* pq = checkQueue(L);
    double outVal;
    int ret = pq_peek(pq, &outVal);
    if (!ret) {
        return 0;
    }
    lua_pushnumber(L, outVal);
    return 1;
}

static int l_pq_size(lua_State* L) {
    PriorityQueue* pq = checkQueue(L);
    lua_pushinteger(L, pq->size);
    return 1;
}

//---------------------------
// 模块入口
//---------------------------
static const struct luaL_Reg pq_methods[] = {
    {"push", l_pq_push},
    {"pop",  l_pq_pop},
    {"peek", l_pq_peek},
    {"size", l_pq_size},
    {NULL, NULL}
};

static const luaL_Reg empty_funcs[] = {
   {NULL, NULL}
};

static int l_pq_init(lua_State* L) {
    // create metatable
    luaL_newmetatable(L, "CPriorityQueueMT");
    lua_pushcfunction(L, l_pq_gc);
    lua_setfield(L, -2, "__gc");
    // methods
    lua_newtable(L);
    luaL_setfuncs(L, pq_methods, 0);
    lua_setfield(L, -2, "__index");
    lua_pop(L,1);

    // module function
    lua_pushcfunction(L, l_pq_create);
    lua_setfield(L, -2, "create");

    return 1;
}

LUALIB_API int luaopen_cpriorityqueue(lua_State* L) {
    luaL_newlib(L, empty_funcs); // new table
    l_pq_init(L);
    return 1;
}

编译与集成

# 1) 编译器 你也可以改成 gcc
CC         = clang
CFLAGS     = -O2 -fPIC -Wall
LDFLAGS    = -shared -undefined dynamic_lookup

# 2) Lua 头文件包含路径 (示例: ../lua)
INCLUDES   = -I ../lua

# 3) 目标名称、源文件
TARGET     = cpriorityqueue.so
SOURCES    = cpriorityqueue.c


# 4) 默认规则: 编译 .so
all: $(TARGET)

$(TARGET): $(SOURCES)
    $(CC) $(CFLAGS) $(INCLUDES) $(LDFLAGS) -o $@ $^

# 如果有更多模块:
# $(TARGET2): $(SOURCES2)
#   $(CC) $(CFLAGS) $(INCLUDES) $(LDFLAGS) -o $@ $^

clean:
    rm -f $(TARGET)
    # rm -f $(TARGET2) ...

  1. 需根据 Skynet 或 Lua 版本包含路径做相应修改 (依赖于lua,需要链接lua库进行编译)INCLUDES   = -I ../lua
  2. 编译 make clean &&  make
  3. 将生成的 cpriorityqueue.so 放在 Skynet 可加载模块目录下(通常 luaclib/ ) 例如:lua_cpath = root .. "luaclib/?.so"  在 skynet 启动配置中 也可以进行配置,确保package.cpath 能搜索到它就可以了


二、在 Skynet lua层中集成流程

Lua层包装 — 之后就可以在lua层直接使用了

-- priority_queue.lua
local cpriorityqueue = require "cpriorityqueue"

local PriorityQueue = {}
PriorityQueue.__index = PriorityQueue

--------------------------------------------------------------------------------
-- Lua层包装
--------------------------------------------------------------------------------
function PriorityQueue:new(initialCap)
    local obj    = setmetatable({}, self)
    -- cObj: C端优先队列指针
    obj._cObj    = cpriorityqueue.create(initialCap or 16)
    obj._nextId  = 1
    obj._storage = {} -- { [id] = task }
    return obj
end

function PriorityQueue:push(task)
    -- 1) 确保有 runTime
    local runTime = task.runTime
    if not runTime then
        error("task must have a runTime field!")
    end
    -- 2) todo 生成id  可以使用唯一 id 生成算法
    local id = self._nextId
    self._nextId = id + 1

    -- 3) 存储到 storage
    self._storage[id] = task

    -- 4) 调用 C push
    local ok = self._cObj:push(runTime, id)
    return ok
end

function PriorityQueue:peek()
    local id = self._cObj:peek()
    if not id then
        return nil
    end
    local t = self._storage[id]
    return t
end

function PriorityQueue:pop()
    local id = self._cObj:pop()
    if not id then
        return nil
    end
    local t = self._storage[id]
    if t then
        self._storage[id] = nil
        return t
    end
    return nil
end

function PriorityQueue:size()
    return cpriorityqueue.size(self._cObj)
end

return PriorityQueue

三、性能评估

  • C语言实现的堆操作复杂度 O(log n) 不变,具体的性能提升(相较于纯 lua 实现来说)还得进一步压测。


四、总结

通过上述方案,就能在Skynet项目中方便地使用C语言的最小堆优先队列通用性方面:

  1. 字段扩展:可轻松在 PQItem 中加更多数据(如 id, priority, callbackRef);

  2. 不止Skynet:只要编译通过并符合Lua C API要求,就可在任何Lua环境中 require。

  3. 使用:同Lua封装 push/pop/peek/size 即可,类似table操作,但性能更优

这样,就完成了“以C编写优先队列并集成到Skynet”的详细流程,一些性能热点可以如此实现,以求更好的性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/959853.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【25考研】人大计算机考研复试该怎么准备?有哪些注意事项?

人大毕竟是老牌985&#xff0c;复试难度不会太低&#xff01;建议同学认真复习&#xff01;没有机试还是轻松一些的&#xff01; 一、复试内容 由公告可见&#xff0c;复试包含笔试及面试&#xff0c;没有机试&#xff01; 二、参考书目 官方无给出参考书目&#xff0c;可参照…

随着监测技术的不断升级,将为智能决策提供强大的数据支持和智能帮助的智慧能源开源了

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;用户仅需在界面上…

vim如何设置自动缩进

:set autoindent 设置自动缩进 :set noautoindent 取消自动缩进 &#xff08;vim如何使设置自动缩进永久生效&#xff1a;vim如何使相关设置永久生效-CSDN博客&#xff09;

字节跳动发布UI-TARS,超越GPT-4o和Claude,能接管电脑完成复杂任务

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

使用eNSP配置GRE VPN实验

实验拓扑 实验需求 1.按照图示配置IP地址 2.在R1和R3上配置默认路由使公网区域互通 3.在R1和R3上配置GRE VPN&#xff0c;使两端私网能够互相访问&#xff0c;Tunne1口IP地址如图 4.在R1和R3上配置RIPv2来传递两端私网路由 实验步骤 GRE VPN配置方法&#xff1a; 发送端&#x…

机器学习-线性回归(对于f(x;w)=w^Tx+b理解)

一、&#x1d453;(&#x1d499;;&#x1d498;) &#x1d498;T&#x1d499;的推导 学习线性回归&#xff0c;我们那先要对于线性回归的表达公示&#xff0c;有所认识。 我们先假设空间是一组参数化的线性函数&#xff1a; 其中权重向量&#x1d498; ∈ R&#x1d437; …

Swing使用MVC模型架构

什么是MVC模式? MVC是一组英文的缩写,其全名是Model-View-Controller,也就是“模型-视图-控制器”这三个部分组成。这三个部分任意一个部分发生变化都会引起另外两个发生变化。三者之间的关系示意图如下所示: MVC分为三个部分,所以在MVC模型中将按照此三部分分成三…

Windows 环境下 Docker Desktop + Kubernetes 部署项目指南

Windows 环境下 Docker Desktop Kubernetes 部署项目指南 一、环境准备二、安装与配置 Kubernetes安装 windows 版的 docker启动 kubernetes安装 windows 版的 kubectl 工具下载 k8s-for-docker-desktop启动 Kubernetes Dashboard 二、在 Kubernetes 上部署项目创建一个 demo …

redis实现lamp架构缓存

redis服务器环境下mysql实现lamp架构缓存 ip角色环境192.168.242.49缓存服务器Redis2.2.7192.168.242.50mysql服务器mysql192.168.242.51web端php ***默认已安装好redis&#xff0c;mysql 三台服务器时间同步&#xff08;非常重要&#xff09; # 下载ntpdate yum -y install…

【Excel】【VBA】Reaction超限点筛选与散点图可视化

【Excel】【VBA】Reaction超限点筛选与散点图可视化 功能概述 这段代码实现了以下功能&#xff1a; 从SAFE输出的结果worksheet通过datalink获取更新数据从指定工作表中读取数据检测超过阈值的数据点生成结果表格并添加格式化创建可视化散点图显示执行时间 流程图 #mermaid-…

Java导出通过Word模板导出docx文件并通过QQ邮箱发送

一、创建Word模板 {{company}}{{Date}}服务器运行情况报告一、服务器&#xff1a;总告警次数&#xff1a;{{ServerTotal}} 服务器IP:{{IPA}}&#xff0c;总共告警次数:{{ServerATotal}} 服务器IP:{{IPB}}&#xff0c;总共告警次数:{{ServerBTotal}} 服务器IP:{{IPC}}&#x…

智能化加速标准和协议的更新并推动验证IP(VIP)在芯片设计中的更广泛应用

作者&#xff1a;Karthik Gopal, SmartDV Technologies亚洲区总经理 智权半导体科技&#xff08;厦门&#xff09;有限公司总经理 随着AI技术向边缘和端侧设备广泛渗透&#xff0c;芯片设计师不仅需要考虑在其设计中引入加速器&#xff0c;也在考虑采用速度更快和带宽更高的总…

RabbitMQ5-死信队列

目录 死信的概念 死信的来源 死信实战 死信之TTl 死信之最大长度 死信之消息被拒 死信的概念 死信&#xff0c;顾名思义就是无法被消费的消息&#xff0c;一般来说&#xff0c;producer 将消息投递到 broker 或直接到queue 里了&#xff0c;consumer 从 queue 取出消息进…

git常用命令学习

目录 文章目录 目录第一章 git简介1.Git 与SVN2.Git 工作区、暂存区和版本库 第二章 git常用命令学习1.ssh设置2.设置用户信息3.常用命令设置1.初始化本地仓库init2.克隆clone3.查看状态 git status4.添加add命令5.添加评论6.分支操作1.创建分支2.查看分支3.切换分支4.删除分支…

私有包上传maven私有仓库nexus-2.9.2

一、上传 二、获取相应文件 三、最后修改自己的pom文件

汽车定速巡航

配备定速巡航功能的车型&#xff0c;一般在方向盘附近设有4~6个按键&#xff08;可能共用键位&#xff09;。 要设置定速巡航&#xff0c;不仅需要方向盘上的按键&#xff0c;还要油门配合。 设置的一般流程&#xff1a; 开关&#xff1a;类似步枪上的“保险”&#xff0c;按…

安宝特方案 | AR在供应链管理中的应用:提升效率与透明度

随着全球化的不断深入和市场需求的快速变化&#xff0c;企业对供应链管理的要求也日益提高。如何在复杂的供应链环境中提升效率、降低成本&#xff0c;并确保信息的透明度&#xff0c;成为了各大行业亟待解决的问题。而增强现实&#xff08;AR&#xff09;技术&#xff0c;特别…

JavaScript(8)-函数

一.什么是函数&#xff1a;执行特定任务的代码块 讲js中需要的公共部分抽取并封装&#xff0c;谁用谁调用&#xff0c;代码复用。 先声明&#xff0c;后调用 使用function函数名需要调用的内容 使用&#xff1a;函数名&#xff08;&#xff09; 二.使用方式 声明&a…

HTML<label>标签

例子 三个带标签的单选按钮&#xff1a; <form action"/action_page.php"> <input type"radio" id"html" name"fav_language" value"HTML"> <label for"html">HTML</label><br&…

4.flask-SQLAlchemy,表Model定义、增删查改操作

介绍 SQLAlchemy是对数据库的一个抽象 开发者不用直接与SQL语句打交道 Python对象来操作数据库 SQLAlchemy是一个关系型数据库 安装 flask中SQLAlchemy的配置 from flask import Flask from demo.user_oper import userdef create_app():app Flask(__name__)# 使用sessi…