即时编译jit和xbyak的基本使用介绍

一般来说,解释型编程语言都是依靠自身运行的虚拟机,在解释程序。有时候语言为了提高运行速度,不会去直接解释程序文本,而是模拟cpu执行方式,将文本代码执行一次翻译,翻译为类似cpu执行的汇编语言去执行。有些语言会模拟出几个CPU的寄存器,有些则使用栈的方式模拟寄存器。无论采用什么方式,基本都是循环读取文件的内容,按照一定规则去解释。这也就导致了,运行速度会慢一点,因为cpu最终执行的是虚拟机,而不是编写的语言。

通常,一个解释型编程语言如果想要运行速度提升,除了对自身代码的优化和对虚拟机的优化外,就是要让代码本身可以让CPU直接能执行语言,也就是说,将编写的语言,经过几次翻译后,变为可以直接在cpu中执行的代码,这个就是jit

实现JIT最重要的一步就是动态执行被翻译成汇编的代码,下面是一个最简单的动态执行汇编的方式

1.动态执行

1.1在linux相关系统中

注:以下代码在cygwin gcc 11.3中调试通过
有如下代码

int func()
{return 19;
}
int main(int, char**) {printf("%d",func());
}

以上代码很简单,执行func函数返回19.然后打印出来

不过,如果func函数没有参与编译,是在程序运行时产生的,比如是从文件读取,或者人工输入,或者源文件在运行时,才被编译为字节码时,这就需要用下面的方式执行了

int main(int, char**) 
{unsigned char code[] = { 0xB8,0x13,0x00,0x00,0x00,0xC3 }; void *mem = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC,MAP_ANON | MAP_PRIVATE, -1, 0);memcpy(mem, code, sizeof(code));int(*func)() = (int(*)())mem;printf("%d",func());munmap(mem,sizeof(code));return 0;
}

逐行解释一下

 { 0xB8,0x13,0x00,0x00,0x00,0xC3 }; 

就是return 19 翻译为汇编后的字节码,B8 是mov,C3是return

mmap是内存映射函数,它的作用就是给开拓一块大小为sizeof(code)的空间。但是这一步还不能用其他分配内存函数实现,比如,如果mmap改成malloc就不行,执行会报错,应为mmap分配的空间,是可以为这个空间设置属性的,比如可以写入PROT_WRITE |,可以执行PROT_EXEC,

int(*func)() = (int(*)())mem;

memcpy就是把要执行的汇编代码复制到这块内存里,这句是要把mmap分配的内存让他指向一个函数执行,然后通过函数执行的方式去调用内存中的代码

munmap就是吧mmap分配的内存释放了

这个就是最简单的一个动态运行方式。

1.2在windows中

如果要在windows下运行的话,mmap函数在windows里好像是没有,就需要换成VirtualAlloc,不过功能是一样的

unsigned char code[] = { 0xB8,0x13,0x00,0x00,0x00,0xC3 };
void* mem = VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(mem, code, sizeof(code));
int(*func)() = (int(*)())mem;
int r =  func();
VirtualFree(code, sizeof(code), MEM_RELEASE);

2.JIT框架xbyak

虽然从汇编翻译字节码的过程也可以自己实现,但这个过程还是比较复杂,汇编指令很多,每个指令都有自己的字节码,要记录下就很困难,但是使用了框架就会省事很多,jit框架很多,dynasm、asmjit等

还有一个 xbyak

xbyak是一个非常简单的可以跨平台的jit框架,使用C++编写,一共四个文件,不到400k。

使用时,直接在项目中直接引入xbyak.h即可。

使用xbyak编写,实际上就是写汇编,比如如下汇编

mov eax,1
ret

改为xbyak,如下

mov(eax,1)
ret()

xbyak的函数和汇编的指令是对应的,大部分写的时候改成函数调用就可以

注:以下代码在win10 vs2015中调试通过,代码仅作测试使用

2.1基本使用

从最简单的开始,首先创建xbyak使用的类

class generatorcode : public Xbyak::CodeGenerator
{
public:generatorcode(void *userPtr = 0, size_t size = Xbyak::DEFAULT_MAX_CODE_SIZE) : Xbyak::CodeGenerator(size, userPtr){}
}

然后用xbyak改写return 19那段代码

class generatorcode : public Xbyak::CodeGenerator
{
public:generatorcode(void *userPtr *= 0, size_t size = Xbyak::DEFAULT_MAX_CODE_SIZE) : Xbyak::CodeGenerator(size, userPtr){}void func(){mov(eax,19);ret();}
}
int main(int, char**) {generatorcode g;g.func();int(*func)() = g.getCode<int(*)()>();printf("%d", func());return 0;
}

运行结果是一样的

虽然加了一个类,但相同功能整个实现过程方便了不少

下面介绍一下xbyak的基本数据操作

2.2常用数据类型

代码如下,分别把int,double ,char* 存到一个struct中

struct datatable {int i;double d;char* c;
};class generatorcode : public Xbyak::CodeGenerator
{
private:datatable * dt;
public:generatorcode(datatable* _dt) : Xbyak::CodeGenerator(){dt = _dt;}void endcode(){ret();}void funcint(int a){mov(eax, dword[&dt]);mov(dword[eax],a);}void funcstr(char* a){mov(eax, dword[&dt]);lea(ecx, dword[a]);mov(dword[eax+16],ecx);}void funcdouble(double *a){mov(eax, dword[&dt]);movsd(xm0, qword[a]);movsd(qword[eax+8], xm0);}
};
int main(int, char**) {datatable * dt = new datatable();generatorcode g(dt);int a = 8;double b = 3.14;char* str = "aaa";g.funcint(8);g.funcdouble(&b);g.funcstr(str);g.endcode();int(*func)() = g.getCode<int(*)()>();func();return 1;
}

结构dt运行结果如下:
在这里插入图片描述

常用的也就这几中数据类型的操作

xbyak的写法更接近NASM的写法,32,64位都支持,也支持MASM的一些语法,比如@@标号,同时也支持AVX,基本上用汇编写程序的指令它这都有。

qword, dword, word,byte 这几个是xbyak数据类型,如果使用的时候确认不了,就写PTR

2.3函数调用

再介绍几个常用的汇编指令,push,pop,call这几个会涉及到函数调用

class generatorcode : public Xbyak::CodeGenerator
{
public:generatorcode() : Xbyak::CodeGenerator(){}void endcode(){ret();}void abc(int a,char* b){push(a);lea(ecx, dword[b]);push(ecx);call(printf);pop(ebx);pop(ebx);}
};
int main(int, char**) {generatorcode g();int a = 8;char* str = "%d";g.abc(a, str);g.endcode();int(*func)() = g.getCode<int(*)()>();func();return 1;
}

以上程序会调用printf打印出8

2.4综合使用

下面从一个语言的的实际出发,写一段代码

int stacklist[100] = {0};
int * intp = &stacklist[0];
void pushint()
{*intp++ = 1;*intp++ = 3;
}
void run()
{int a = *(intp-2);int b = *(intp-1);*(intp - 2) = a + b;intp--;if (*(intp - 1) > 10){printf(">10");}else{printf("<10");}
}
int main(int, char**) {pushint();run();return 1;
}

这个是模拟的了一个栈,然后分别压入两个int数据1,3,然后通过模拟sp指针,将两个数去取出,加在一起,如果结果大于10显示>10 ,小于10就显示<10

首先用内嵌ASM写一次看看

int stacklist[100] = {0};
int * intp = &stacklist[0];
int main(int, char**) {char * a10 = "<10";char * b10 = ">10";_asm {	mov eax, intpmov[eax], 1add eax,4mov[eax],3add eax,4mov ecx,dword ptr [eax - 8]mov edi,dword ptr [eax - 4]add edi, ecxmov [eax-8],edisub eax,4mov edi,dword ptr [eax-4]	cmp edi,10jge l10push a10call printfpop eaxjmp lend
l10:push b10call printfpop eax
lend:}
}

然后在使用xbyak写一次

int stacklist[100] = { 0 };
int * intp = &stacklist[0];
class generatorcode : public Xbyak::CodeGenerator
{
private:char * a10 = "<10";char * b10 = ">10";
public:generatorcode() : Xbyak::CodeGenerator(){}void endcode(){ret();}void abc(){mov(eax, dword[&intp]);mov(dword[eax], 1);add(eax, 4);mov(dword[eax], 3);add(eax, 4);mov(ecx, dword[eax - 8]);mov(edi, dword[eax - 4]);add(edi, ecx);mov(dword[eax - 8], edi);sub(eax, 4);mov(edi, dword[eax - 4]);cmp(edi, 10);jge(".l10");push(dword[&a10]);call(printf);pop(eax);jmp(".lend");
L(".l10");push(dword[&b10]);call(printf);pop(eax);
L(".lend");}
};
int main(int, char**) {generatorcode g;g.abc();g.endcode();int(*func)() = g.getCode<int(*)()>();func();return 1;
}

以上汇编代码仅作演示使用,实际使用中应该尽可能减少使用寄存器,将局部使用的数据存入栈内

可以看出来,写xbyak实际和写汇编几乎一样,除了几条有稍微差别外,写法基本相同,如果是按nasm的写法的话,可能就更接近了

事实上一个编译型语言做成JIT能不能更快,或者快多少,还是要看翻译后的汇编代码量,越少的代码量、越简单的数据类型,运行就会越快

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【vue3】实现数据响应式(ref、shallowRef、trigger、reactive、shallowReactive、toRef、toRefs)

一、ref、shallowRef、trigger ref支持所有类型 可以粗略理解为 ref shallowRef triggerRef 1、通过ref获取dom元素 <p ref"_ref">这是ref获取dom元素</p>import {ref,shallowRef, triggerRef} from vueconst _ref ref()console.log(_ref.value?.i…

【HTML】web worker

Web Worker是HTML5中的一项技术&#xff0c;可以在后台运行JavaScript代码&#xff0c;以提高Web应用程序的性能并改善用户体验。它允许在独立的线程中执行耗时的操作&#xff0c;而不会阻塞主线程。 主线程是浏览器用来渲染页面、处理用户交互和执行JavaScript代码的线程。然…

QT基础 QChart绘制折线

目录 1.简单折线 2.数学折线 3.可滑动折线 1.简单折线 //![1] //! 折现段坐标QLineSeries *series new QLineSeries(); //![1]//![2] //! 添加点series->append(0, 6);series->append(2, 4);series->append(3, 8);series->append(7, 4);series->append(10, 5)…

Keil 厂商DFP pack实现原理

要想在Keil中方便地通过界面点击来导入芯片厂商提供的库&#xff0c;通常需要安装厂商提供的pack&#xff0c;如下图&#xff1a; 这个过程是如何实现的&#xff1f; 双击安装pack后&#xff0c;pack文件会将自身的内容解压到下图的目录&#xff0c;命名为厂商名字的文件夹&…

Kubernetes新增节点

1. K8S节点Hosts及防火墙设置 node3节点进行如下配置&#xff1a; #添加hosts解析&#xff1b; cat >/etc/hosts<<EOF 127.0.0.1 localhost localhost.localdomain 192.168.1.146 master1 192.168.1.147 node3 EOF #临时关闭selinux和防火墙&#xff1b; sed -i /SE…

canvas基础2 -- 形状

七巧板 七巧板本质上就是 分别由几个直线 拼成一个个图形&#xff0c;再将这些图形结合起来 var tangram [{ p: [{ x: 0, y: 0 }, { x: 800, y: 0 }, { x: 400, y: 400 }], color: "#caff67" },{ p: [{ x: 0, y: 0 }, { x: 400, y: 400 }, { x: 0, y: 800 }], col…

AlphaPose Pytorch 代码详解(一):predict

前言 代码地址&#xff1a;AlphaPose-Pytorch版 本文以图像 1.jpg&#xff08;854x480&#xff09;为例对整个预测过程的各个细节进行解读并记录 python demo.py --indir examples/demo --outdir examples/res --save_img1. YOLO 1.1 图像预处理 cv2读取BGR图像 img [480,…

Redis 的过期键 | Navicat 技术干货

Redis 是一种高性能的内存数据存储&#xff0c;以其速度和多功能性而闻名。其中一个有用的特性是为键设置过期时间的功能。在 Redis 中&#xff0c;为键设置过期时间对于管理数据和确保过时或临时数据自动从数据库中删除是至关重要的。在本文中&#xff0c;我们将探讨在 redis-…

4、在 CentOS 8 系统上安装 pgAdmin 4

pgAdmin 4 是一个开源的数据库管理工具&#xff0c;专门用于管理和操作 PostgreSQL 数据库系统。它提供了一个图形用户界面&#xff08;GUI&#xff09;&#xff0c;使用户能够轻松地连接到 PostgreSQL 数据库实例&#xff0c;执行 SQL 查询&#xff0c;管理数据库对象&#xf…

STM32物联网基于ZigBee智能家居控制系统

实践制作DIY- GC0169-ZigBee智能家居 一、功能说明&#xff1a; 基于STM32单片机设计-ZigBee智能家居 二、功能介绍&#xff1a; 1个主机显示板&#xff1a;STM32F103C最小系统ZigBee无线模块OLED显示器 语音识别模块多个按键ESP8266-WIFI模块&#xff08;仅WIFI版本有&…

java模拟GPT流式问答

流式请求gpt并且流式推送相关前端页面 1&#xff09;java流式获取gpt答案 1、读取文件流的方式 使用post请求数据&#xff0c;由于gpt是eventsource的方式返回数据&#xff0c;所以格式是data&#xff1a;&#xff0c;需要手动替换一下值 /** org.apache.http.client.metho…

QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样 Chapter1 QT自制软键盘 最完美、最简单、跟自带虚拟键盘一样一、本自制虚拟键盘特点二、windows打开系统自带软键盘三、让…