仓颉编程语言 -- 初识(二)

4、卓越性能

仓颉语言通过值类型、多层级静态分析优化和超轻量运行时,在计算机语言基准测试Benchmarks Game上,相比业界同类语言取得了较为明显的性能优势。
在这里插入图片描述

4.1 静态编译优化

仓颉编译采用模块化编译,编译流程间通过IR作为载体,不同编译优化之间,做到互相不影响。对于编译优化的适配,编译流程的调整,拥有更高的自由度。

仓颉语言使用静态编译手段,将仓颉程序、核心库代码等编译成机器代码,加速程序运行速度。

4.1.1 GC相关优化

仓颉静态编译中,添加了许多运行时联合优化。例如对于堆上对象读写的优化、堆对象创建的优化、以及堆内存管理信号机制的优化等。静态分析和运行时的联合优化,加快了仓颉程序在对象的创建、读写、成员函数调用等方面的运行速度。

仓颉静态后端同时对于堆对象的访问时,使能了向量化优化,保证数据读写、运算速率,尽量减少GC Barrier对性能的影响。对堆对象的活跃作用域的分析,也保证了静态后端能够对堆对象的分配地址拥有决定权,无论在堆、栈或常量区,静态后端能根据对象特性来进行分配优化。

仓颉对于栈上引用的精确记录,能够加快GC 信息采集速度。精确栈对象的记录,减少了垃圾回收根集合的数量,避免了对象指针的冗余地址判断。在扫描和fix阶段,保证了GC程序高效运行。

结合GC功能,仓颉语言对于对象的创建、读写上进行了fastPath优化。如下图所示,在编译访存操作时,生成快速路径和高效判断快速路径的指令,减少性能开销。

在这里插入图片描述

4.1.2 逃逸分析

仓颉语言在做全局分析优化时,增加了引用的逃逸分析。对于引用的类型,仓颉语言分析该引用的生命周期,对于未逃逸出其所在函数的引用,可以采用栈上分配优化。如下代码所示,其中包含了部分逃逸分析结果。

class B {
   }
class A {
   
  var a : Int64 = 0
  var b : B = B()
}

var ga : A = A()

func test1(var a : A) {
   
  a.a = 10
}

func test2(var a : A) {
   
  ga = a // escape to global
}

func test3(var a : A, var b : B) {
   
  a.b = b
}

main() {
   
  var instance : A = A() // alloca on stack, not escape out this func
  instance.a = 10
  var instance1 : A = A() // alloca on stack, test1 not escape param a
  test1(instance1)
  var instance2 : A = A() // gc malloc in heap, test2 escapa param a
  test2(instance2)
  var instance3 : B = B() // alloca on stack, instance3 store into instance1, but instance1 not escaped.
  test3(instance1, instance3)
  var instance4 : B = B()  // gc malloc in heap, instance4 store int instance2 and instance2 escaped to global.
  test3(instance2, instance4)
}

通过栈上分配优化,可以直接缩减自动管理内存的GC压力,减少堆上分配内存和频率,降低垃圾回收频率。对于堆上内存的读写屏障,也会因为栈上分配从而变成直接的数据存取,加快了内存访问速度。对象栈上分配后,对于栈上内存,又可以额外采用例如SROA,DSE等优化措施,减少内存读写次数。

4.1.3 类型分析/去虚化

仓颉语言支持全局类型静态分析和结合Profile的类型预测。仓颉语言支持类型继承,支持虚函数、接口函数调用,对于虚函数、接口函数的调用,相比较Direct Call增加了额外的查找、访问开销。

对于全局引用、局部引用、过程间引用等,通过静态分析的方式,仓颉语言将部分虚函数调用改写为Direct Call,加速函数调用,提升函数内联等优化机会。

在PGO模式下,仓颉语言支持虚函数调用的类型、数量统计,通过Profile信息捕捉到的热类型、热调用部分,通过保守去虚化的方式,加速函数调用和程序执行。
在这里插入图片描述

4.2 值类型

仓颉语言引入了值类型对象,值类型的局部变量在读写时无需GC相关屏障,在进行内存读写时,能够直接访问,无需考虑引用信息的变化。合理利用值类型语义,能有效加速程序运行。

在这里插入图片描述
值类型提供了更多数据排布和访问的方式。通过合理的数据结构设计,使得数据在访问上能够拥有优秀的空间/时间局部性,在运算、访问等操作上能够带来更大优势。如下图所示,类A的数组在访问数组内成员的成员变量时,需要进行2次load,而对于值类型的数组,对于A里成员变量的访问时,仅需1次load。

在这里插入图片描述
**OSR(On Stack Replacement)**优化对于值类型非常友好,在合理OSR的情况下,部分值类型数据能够直接打散到寄存器中,对于数据访问、运算等带来更大优势。例如下述示例,值类型SA对象,被打散成a和b,而a和b都可以在寄存器中表示,而不用再进行重复的load。后续再通过常量传播可以直接将a和b用常量表示。

struct A {
   
  var a : UInt64 = 10
  var b : UInt64 = 100
}

main() {
   
  var SA = A()
  var sum = 0
  for (i in 0..100) {
   
    sum += SA.a
  }
  return sum + SA.b
}

=>
main() {
   
  var a = 10
  var b = 100
  var sum = 0
  for (I in 0..100) {
   
    sum += a
  }
  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值