Lua与C语言接口编程实战指南:打造高性能、灵活的程序

简介: 本文深入介绍了 Lua 与 C 语言的交互机制,重点分析了 Lua 作为胶水语言在嵌入式系统、游戏开发(如 Skynet、OpenResty)中的应用。内容涵盖 Lua 环境搭建、虚拟栈管理、C 与 Lua 的相互调用、闭包、Userdata 和注册表的使用等核心技术,并结合代码示例讲解了如何在实际项目中实现 Lua 与 C 的高效交互,适合希望掌握 Lua 扩展与嵌入开发的工程师参考学习。

它们是怎么交互的。skynet、openresty 都是深度使用 lua 语言的典范;学习 lua 不仅仅要学习基本用法,还要学会使用 c 与 lua 交互,这样才学会了 lua 作为胶水语言的精髓。

openresty(nginx + lua):openresty使用多进程,每个进程都有自己的lua虚拟机。

skynet中调用层次:skynet是多线程的,actor的调度和执行由线程池分配。

二、Lua环境搭建

(1)下载lua包并解压:

代码语言:Bash

自动换行

AI代码解释

wget -c http://www.lua.org/ftp/lua-5.3.0.tar.gz
tar zxvf lua-5.3.0.tar.gz

(2)编译安装:

代码语言:Bash

自动换行

AI代码解释

cd lua-5.3.0
make linux
sudo make install

(3)测试:

代码语言:Bash

自动换行

AI代码解释

lua -v
# 或者
lua

代码语言:Lua

自动换行

AI代码解释

Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio 
>print("Hello World!") 
Hello World!

(4)使用lua库时,编译带上:

代码语言:Bash

自动换行

AI代码解释

-llua -ldl -lm

三、虚拟栈

  1. 栈中只能存放 lua 类型的值,如果想用 c 的类型存储在栈中,需要将 c 类型转换为 lua 类型。
  2. lua调用c的函数都得到一个新的栈,独立于之前的栈。
  3. c 调用 lua,每一个协程都有一个栈;需要维护这个栈。
  4. c 创建虚拟机时,伴随创建了一个主协程,默认创建一个虚拟栈。
  5. 无论何时 Lua 调用 C , 它都只保证至少有 LUA_MINSTACK 这么多的堆栈空间可以使用。 LUA_MINSTACK 一般被定义为 20 ,因此,只要不是不断的把数据压栈, 通常不用关心堆栈大小。
  6. c调用lua时,一定确保不要出现栈溢出问题,关注栈中有多少数据,不能超过栈的大小。特别是使用完函数后,一定要把栈清空。

对lua不熟悉可以查看Lua参考手册查看接口函数,里面都提供了c语言的api和lua的api。

四、c语言调用lua的函数

c代码只需要编译一次,lua可以随时改动;因为lua是动态语言。嵌入lua的好处是c只需要写一次代码,aly.onfirmax.com22编译一次程序,所有的变化都可以通过修改lua的代码,重启程序就可以了。当然,也可以在c语言里面添加逻辑实现自动检测lua文件的改变并自动加载新的lua文件。

4.1、实现步骤

(1)包含头文件:

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

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

(2)创建lua虚拟机和加载lua库:

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

lua_State *L=luaL_newstate();   //创建lua虚拟机
luaL_openlibs(L);               //加载lua库,比如math库、table库等

(3)加载lua文件到c语言的内存空间:

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

luaL_dofile(L,filename);  //加载lua文件到c语言内存中,进行语法检查,不会编译

(4)取全局变量并压栈传参数:

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

lua_getglobal(L, func_name); // 查找lua文件中的全局函数,并压入虚拟栈
lua_pushinteger(L, 1);      // 压栈,传入参数

(4)执行函数,lua_call 的参数中第二个是参入参数个数,第三个是返回值个数:

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

lua_call(L, 1, 0);          // 栈弹出,执行函数

(5)关闭lua:

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

lua_close(L);

4.2、完整示例代码

vm_test.c

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
static void call_func(lua_State *L, const char* funcname)
{
    lua_getglobal(L, funcname); // 查找lua文件中的全局函数,并压入虚拟栈
    lua_pushinteger(L, 1);      // 压栈,传入参数
    lua_call(L, 1, 0);          // 调用方法
}
int main(int argc,char **argv)
{
    lua_State *L=luaL_newstate();   //创建lua虚拟机
    luaL_openlibs(L);               //加载lua库,比如math库、table库等
    if(argc>1)
    {
        lua_pushboolean(L,1);
        lua_setfield(L,LUA_REGISTRYINDEX,"LUA_NOENV");
        if(LUA_OK!=luaL_dofile(L,argv[1]))  //加载lua文件到c语言内存中,进行语法检查,不会编译
        {
            const char * err=lua_tostring(L,-1);
            fprintf(stderr,"err:\t%s\n",err);
            return -1;
        }
    }
    
    // 执行lua的方法、函数
    call_func(L, "Init");
    call_func(L, "Loop");
    call_func(L, "Release");
    // 删除虚拟机
    lua_close(L);
    return 0;
}

vm_test.lua

代码语言:Lua

自动换行

AI代码解释

package.cpath = "luaclib/?.so"
function Init(args)
    print("call [init] function", args)
end
function Loop()
    print("call [loop] function")
    for k, v in ipairs({1,2,3,4,5}) do
        print("value = " .. v)
    end
end
function Release()
    print("call [release] function")
end

编译:

代码语言:Bash

自动换行

AI代码解释

gcc -o vm_test vm_test.c -llua -ldl -lm

执行:

代码语言:Bash

自动换行

AI代码解释

./vm_test vm_test.lua

执行结果:

代码语言:Bash

自动换行

AI代码解释

call [init] function    1
call [loop] function
value = 1
value = 2
value = 3
value = 4
value = 5
call [release] function

五、Lua调用c语言的函数

lua只识别动态库,c语言需要编译被调用模块,然后编译成动态库。那么lua怎么识别c语言的动态库的函数呢?lua只识别以luaopen开头的函数。

5.1、原理

  1. c语言编写模块,编译成动态库。aly.sichelle.com22
  2. lua加载c语言的动态库:.so,.dll,.dylib。
  3. 约定读取动态库中以luaopen_*命名的函数。
  4. lua获取模块对象后,通过函数名可以找到c函数地址,从而调用c代码

5.2、实现步骤

(1)c语言中实现一个函数。

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

static int
lecho (lua_State *L) {
    const char* str = lua_tostring(L, -1);
    fprintf(stdout, "%s\n", str);
    return 0;
}

(2)创建一个导出给lua使用的数组,类型是luaL_Reg。

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

static const luaL_Reg l[] = {// 导出给lua使用数组
  {"echo", lecho},
  {NULL, NULL},
};

(3)c语言实现一个luaopen开头的函数,创建一张新的表,并预分配足够保存下函数指针数组内容的空间。

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

int
luaopen_tbl_c(lua_State *L) { // local tbl = require "tbl.c"
    // 创建一张新的表,并预分配足够保存下数组 l 内容的空间
  // luaL_newlibtable(L, l);
  // luaL_setfuncs(L, l, 0);
    luaL_newlib(L, l);
  return 1;
}

(4)lua中导入c语言动态库并使用函数。

代码语言:Lua

自动换行

AI代码解释

package.cpath = "luaclib/?.so"
local so = require "tbl.c"

(5)编译c语aly.segalinc.com11

5.3、从lua角度看调用过程

(1)lua首先会进行动态库的导入,即

代码语言:Lua

自动换行

AI代码解释

local so=require "table.c"

(2)Lua就会去查找动态库中以luaopen_*开头的函数,*就是展开了的c文件名,比如展开为luaopen_table_c的函数名。(3)找到函数后就执行函数,函数里面新建了一张函数地址表(函数数组),然后以table形式传给变量so。(4)接下来就可以使用c语言的函数了。

5.4、完整示例代码

注意:在Linux上直接使用apt-get安装的lua是没有自动安装lua库的,需要下载lua的源码编译安装。tbl.c

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
static int
lecho (lua_State *L) {
    const char* str = lua_tostring(L, -1);
    fprintf(stdout, "%s\n", str);
    return 0;
}
static const luaL_Reg l[] = {// 导出给lua使用数组
  {"echo", lecho},
  {NULL, NULL},
};
int
luaopen_tbl_c(lua_State *L) { // local tbl = require "tbl.c"
    // 创建一张新的表,并预分配足够保存下数组 l 内容的空间
  // luaL_newlibtable(L, l);
  // luaL_setfuncs(L, l, 0);
    luaL_newlib(L, l);
  return 1;
}

test.lua

代码语言:Lua

自动换行

AI代码解释

package.cpath = "luaclib/?.so"
local so = require "tbl.c"
function Init(args)
    print("call [init] function", args)
end
function Loop()
    print("call [loop] function")
    for k, v in ipairs({1,2,3,4,5}) do
        so.echo(v)
    end
end
function Release()
    print("call [release] function")
end

Makefile示例:

代码语言:Bash

自动换行

AI代码解释

# $@ 目标文件 $^ 所有的依赖文件 $< 第一个依赖文件
# -Wl,-E 
PLAT ?= linux
# 在Makefile中,.PHONY后面的target表示的也是一个伪造的target, 而不是真实存在的文件target;
# 注意Makefile的target默认是文件。 shell
.PHONY : clean all cleanall
LUA_CLIB_PATH ?= luaclib
LUA_CLIB_SRC ?= lualib-src
# LUA_CLIB = tbl uv reg1 reg2 ud
LUA_CLIB = tbl
LUA_INC ?= ./lua/src
SHARED := -fPIC --shared
EXPORT := -Wl,-E
ifeq ($(PLAT), macosx)
  SHARED := -fPIC -dynamiclib -Wl,-undefined,dynamic_lookup
endif
CFLAGS = -g -O2 -Wall -I$(LUA_INC)
CC ?= gcc
LUA_STATICLIB := lua/src/liblua.a
all : \
  $(LUA_STATICLIB) \
  test-vm \
  $(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so)
$(LUA_STATICLIB) :
  cd lua && $(MAKE) CC='$(CC) -std=gnu99' $(PLAT)
test-vm : test-vm.c
  $(CC) $(CFLAGS) $^ -o $@ -L$(LUA_INC) $(EXPORT) -llua -ldl -lm
$(LUA_CLIB_PATH) :
  mkdir -p $(LUA_CLIB_PATH)
$(LUA_CLIB_PATH)/tbl.so : $(LUA_CLIB_SRC)/lua-tbl.c | $(LUA_CLIB_PATH)
  $(CC) $(CFLAGS) $(SHARED) $^ -o $@ 
clean:
  rm -f $(LUA_CLIB_PATH)/*.so
  rm -f test-vm
cleanall :
  rm -f $(LUA_CLIB_PATH)/*.so
  cd lua && $(MAKE) clean
  rm -f test-vm

六、C 闭包

  1. 通过 lua_pushcclosure 用来创建 C 闭包;
  2. 通过 lua_upvalueindex 伪索引来获取上值(lua 值);
  3. 可以为多个导出函数(c 导出函数给 lua 使用)共享上值,这样可以少传递一个参数。

示例:

lua-uv.c

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
// 闭包实现:  函数 + 上值  luaL_setfuncs
// lua_upvalueindex(1)
// lua_upvalueindex(2)
static int
lecho (lua_State *L) {
  lua_Integer n = lua_tointeger(L, lua_upvalueindex(1));
    n++;
    const char* str = lua_tostring(L, -1);
    fprintf(stdout, "[n=%lld]---%s\n", n, str);
    lua_pushinteger(L, n);
    lua_replace(L, lua_upvalueindex(1));// 更新UpValue
    return 0;
}
static const luaL_Reg l[] = {
  {"echo", lecho},
  {NULL, NULL},
};
int
luaopen_uv_c(lua_State *L) { // local tbl = require "tbl.c"
  luaL_newlibtable(L, l);// 1
    lua_pushinteger(L, 0);// 2 ,绑定上值UpValue
  luaL_setfuncs(L, l, 1);// 上值
    // luaL_newlib(L, l);
  return 1;
}

test-uv.lua

代码语言:Lua

自动换行

AI代码解释

package.cpath = "luaclib/?.so"
local so = require "uv.c"
so.echo("hello world1")
so.echo("hello world2")
so.echo("hello world3")
so.echo("hello world4")
so.echo("hello world5")
so.echo("hello world6")
so.echo("hello world7")
so.echo("hello world8")
so.echo("hello world9")
--./lua/src/lua test-uv.lua

用lua来执行:

代码语言:Bash

自动换行

AI代码解释

./lua/src/lua test-uv.lua

执行结果:

代码语言:Bash

自动换行

AI代码解释

[n=1]---hello world1
[n=2]---hello world2
[n=3]---hello world3
[n=4]---hello world4
[n=5]---hello world5
[n=6]---hello world6
[n=7]---hello world7
[n=8]---hello world8
[n=9]---hello world9

七、userdata在c语言的使用

userdata 是指向一块内存的指针,该内存由 lua 来创建,通过void *lua_newuserdatauv(lua_State *L, size_t sz,int nuvalue) 这个函数来创建。注意:这块内存大小必须是固定的,不能动态增加,但是这块内存中的指针指向的数据可以动态增加。lua 5.4的 userdata 可以绑定若干个 lua 值(又称uservalue)。

userdata 与 uservalue 的关系是引用关系,也就是 uservalue 的生命周期与 userdata 的生命周期一致。

  1. int lua_getiuservalue (lua_State *L, int idx, int n) 来获取绑定在 userdata 上的 uservalue。
  2. int lua_setiuservalue (lua_State *L, int idx, int n) 来设置 userdata 上的 uservalue。

使用示例:lua-ud.c

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct log {
    int count;
};
static int
lagain(lua_State *L) {
    struct log *p = (struct log *)luaL_checkudata(L, 1, "ud.log");  //检查userdata, 返回userdata地址
    lua_getuservalue(L, -1);
    const char* str = lua_tostring(L, -1);
    fprintf(stdout, "ud[n=%d]----%s\n", p->count, str);
    return 0;
}
static int
lecho(lua_State *L) {
    struct log *p = (struct log *)luaL_checkudata(L, 1, "ud.log");//检查userdata, 返回userdata地址
    const char* str = lua_tostring(L, -1);  // 获取UpValue
    p->count++;
    lua_setuservalue(L, -2);
    fprintf(stdout, "ud[n=%d]----%s\n", p->count, str);
  return 0;
}
static int
lnew (lua_State *L) {
  // lua虚拟机分配一块内存作为userdata的空间
    struct log *q = (struct log*)lua_newuserdata(L, sizeof(struct log));
    q->count = 0;
    lua_pushstring(L, "");    //绑定UpValue,初始值是“”
    lua_setuservalue(L, -2);  // 从栈上弹出一个值并将其设为给定索引处用户数据的关联值
  if (luaL_newmetatable(L, "ud.log")) { // 如果注册表中已存在键 "ud.log",返回0。 否则为用户数据的元表创建一张新表
        // 数组保存函数地址
        luaL_Reg m[] = {
      {"echo", lecho},
            {"again", lagain},
      {NULL, NULL},
    };
    //  创建一张新的表,并预分配足够保存下数组 m 内容的空间
    luaL_newlib(L, m);
    // 设置元表__index段,而 -2是栈顶的索引对应的那个值,当key不存在或不是表时调用m表。
    lua_setfield(L, -2, "__index");
        lua_setmetatable(L, -2);
    }
    return 1;
}
// 数组 l 内容,保存函数地址
static const luaL_Reg l[] = {
  {"new", lnew},
  {NULL, NULL},
};
int
luaopen_ud_c(lua_State *L) {
  //  创建一张新的表,并预分配足够保存下数组 l 内容的空间
    luaL_newlib(L, l);
  return 1;
}

test-ud.lua

代码语言:Lua

自动换行

AI代码解释

package.cpath = "luaclib/?.so"
local so = require "ud.c"
local ud = so.new()
ud:echo("hello world1")
ud:again()
ud:echo("hello world2")
ud:again()
ud:echo("hello world3")
ud:again()
ud:echo("hello world4")
ud:again()
ud:echo("hello world5")
ud:again()
ud:echo("hello world6")
ud:again()
ud:echo("hello world7")
ud:again()
ud:echo("hello world8")
ud:again()
ud:echo("hello world9")
ud:again()
--./lua/src/lua test-ud.lua

用lua来测试:

代码语言:Bash

自动换行

AI代码解释

./lua/src/lua test-ud.lua

执行结果:

代码语言:Bash

自动换行

AI代码解释

[n=1]---hello world1
[n=1]---hello world1
[n=2]---hello world2
[n=2]---hello world2
[n=3]---hello world3
[n=3]---hello world3
[n=4]---hello world4
[n=4]---hello world4
[n=5]---hello world5
[n=5]---hello world5
[n=6]---hello world6
[n=6]---hello world6
[n=7]---hello world7
[n=7]---hello world7
[n=8]---hello world8
[n=8]---hello world8
[n=9]---hello world9
[n=9]---hello world9

八、注册表在c语言的使用

可以用来在多个 c 库中共享 lua 数据(包括 userdata 和lightuserdata )。

  1. 一张预定义的表,用来保存任何 c 代码想保存的 lua 值。
  2. 使用 LUA_REGISTRYINDEX 来索引。

Lua有一个全局表,有一个LUA_REGISTRYINDEX的key,LUA_REGISTRYINDEX的value又是一个表,专门用来存放全局数据([key]=userdata)。

使用示例:lua-reg1.c

代码语言:C

代码运行次数:0

自动换行运行

AI代码解释

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
static int
lecho (lua_State *L) {
    lua_getfield(L, LUA_REGISTRYINDEX, "reg.c"); 
    // 此时栈顶放了一张表如下
    //        {["reg.c"] = {1000, 2000}}
    lua_rawgeti(L, -1, 1);
    // -1 是读取栈顶的表  1 是指读这个表中索引为1 的field
    // 相当于 取出 1000 放在栈顶
    // 此时 栈上 1 的位置 为 table   2的位置为 1000
    lua_Integer n = lua_tointeger(L, -1);
    // 将栈顶的值转化为整数
    n++;
    lua_pop(L, 1);
    // 将栈顶的值 pop 出
    // 此时栈上只有一个 table
    lua_pushinteger(L, n);
    // 将运算后的 n push 到栈上
    // 此时栈上 1 的位置为 table   2的位置为 1001
    lua_rawseti(L, -2, 1);
    // 将 栈顶的值 设置到 1 位置中table 索引为 1的位置
    // 此时 table = {1001,2000}
    // 此时栈上只有一个 table 因为设置的时候 把栈顶的值丢掉了
    const char* str = lua_tostring(L, 1);
    fprintf(stdout, "reg1[n=%lld]----%s\n", n, str);
    return 0;
}
static const luaL_Reg l[] = {
  {"echo", lecho},
  {NULL, NULL},
};
int
luaopen_reg1_c(lua_State *L) { // local tbl = require "tbl.c"
    // 创建一张新的表,并预分配足够保存下数组 l 内容的空间
  // luaL_newlibtable(L, l);
  // luaL_setfuncs(L, l, 0); {[reg.c] = {1000,2000}}
    // {
//        ["reg.c"] = {1000, 2000}
    // }
    if (lua_getfield(L, LUA_REGISTRYINDEX, "reg.c") == LUA_TNIL) {
        lua_createtable(L, 2, 0); //1
        lua_pushinteger(L, 1000); //2
        lua_rawseti(L, -2, 1); //1   {1000}
        lua_pushinteger(L, 2000);//2
        lua_rawseti(L, -2, 2);// 1  [reg.c] = {1000,2000}
        lua_setfield(L, LUA_REGISTRYINDEX, "reg.c");
        fprintf(stdout, "luaopen_reg1_c 注册表 reg.c\n");
    }
    luaL_newlib(L, l);
  return 1;
}

test-reg.lua

代码语言:Lua

自动换行

AI代码解释

package.cpath = "luaclib/?.so"
local so1 = require "reg1.c"
so1.echo("hello world")
so1.echo("hello world")
so1.echo("hello world")

九、总结

  1. 热更新,在c代码中实现当lua文件发生改变时自动创建一个新的lua虚拟机加载新的lua文件,加载完成后销毁旧的lua虚拟机,就可以不需要重启程序。
  2. lua函数其实是c语言类型的函数,但是lua函数比c语言函数多了UpValue,即lua=c+UpValue。
  3. 牢记,c调用lua的函数时,一定要维护虚拟栈;lua调用c时不需要维护虚拟栈(因为每次调用都会生成一个新的栈)。
相关文章
|
缓存 网络协议 5G
剖析KCP以及KCP在游戏中是如何使用的
剖析KCP以及KCP在游戏中是如何使用的
|
自然语言处理 安全 C++
【C++ 格式化输出 】C++20 现代C++格式化:拥抱std--format简化你的代码
【C++ 格式化输出 】C++20 现代C++格式化:拥抱std--format简化你的代码
10016 4
|
算法 安全 Java
【C/C++ 实用工具】静态代码检测工具和平台的一览
【C/C++ 实用工具】静态代码检测工具和平台的一览
1340 0
|
JSON Ubuntu 开发者
ubuntu 22安装lua环境&&编译lua cjson模块
通过上述步骤,可以在 Ubuntu 22.04 系统上成功安装 Lua 环境,并使用 LuaRocks 或手动编译的方式安装 lua-cjson 模块。本文详细介绍了每一步的命令和操作,确保每一步都能顺利完成,适合需要在 Ubuntu 系统上配置 Lua 开发环境的开发者参考和使用。
1756 13
|
6月前
|
Web App开发 缓存 Rust
|
8月前
|
存储 SQL Java
数据存储使用文件还是数据库,哪个更合适?
数据库和文件系统各有优劣:数据库读写性能较低、结构 rigid,但具备计算能力和数据一致性保障;文件系统灵活易管理、读写高效,但缺乏计算能力且无法保证一致性。针对仅需高效存储与灵活管理的场景,文件系统更优,但其计算短板可通过开源工具 SPL(Structured Process Language)弥补。SPL 提供独立计算语法及高性能文件格式(如集文件、组表),支持复杂计算与多源混合查询,甚至可替代数据仓库。此外,SPL 易集成、支持热切换,大幅提升开发运维效率,是后数据库时代文件存储的理想补充方案。
|
11月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
1073 29
|
人工智能 NoSQL C语言
程序又崩了?一招精准定位段错误!
在C/C++开发中,程序崩溃(如段错误)是常见问题,但快速定位崩溃原因却颇具挑战。本文介绍了一种精准定位崩溃问题的方法:通过捕获异常信号(如SIGSEGV),结合`backtrace()`和`abi::__cxa_demangle()`打印堆栈信息,从而快速定位问题接口。相比增加日志或生成coredump文件,此方法更高效且无副作用。实现时需注意编译选项(如`-O0 -g -rdynamic`)以保留符号信息,并处理C++名称修饰问题。
704 0
|
机器学习/深度学习 前端开发 JavaScript
WebAssembly:让前端性能突破极限的秘密武器
WebAssembly(简称 WASM)作为前端开发的性能加速器,能够让代码像 C++ 一样在浏览器中高速运行,突破了 JavaScript 的性能瓶颈。本文详细介绍了 WebAssembly 的概念、工作原理以及其在前端性能提升中的关键作用。通过与 JavaScript 的配合,WASM 让复杂运算如图像处理、3D 渲染、机器学习等在浏览器中流畅运行。文章还探讨了如何逐步集成 WASM,展示其在网页游戏、高计算任务中的实际应用。WebAssembly 为前端开发者提供了新的可能性,是提升网页性能、优化用户体验的关键工具。
6773 2
WebAssembly:让前端性能突破极限的秘密武器
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
1282 0