首页 > 其他分享 >Nim 概念 Concept 对性能的影响

Nim 概念 Concept 对性能的影响

时间:2023-12-05 11:24:05浏览次数:53  
标签:index Concept int32 性能 Nim object getValue int64 proc

Nim 概念 Concept 对性能的影响

继上一篇文章《C# 泛型编译特性对性能的影响》后,我又研究了 Nim 语言相关的设计,由于 Nim 语言与 C# 语言有些差异,比如Nim 没有接口,也没有直接的 class 关键字,所以某些实现是变通的办法。

概念 Concept

在Nim中没有 Interface 的概念,虽然有多次提案,但似乎语言设计者一直拒绝这一设计,他们提供了 Concept 这一方案。他与C#的interface的相似点包括:

  • conceptinterface都用于定义抽象类型和行为规范,允许在没有提供具体实现的情况下定义方法签名。
  • 两者都支持多态,允许不同的类型实现相同的接口或概念,并以相同的方式被操作或使用。

但也有不同之处,比如:

  • 在Nim的concept中,方法声明不需要关键字(如fn),而是直接指定方法的签名。在C#中,interface中的方法声明需要使用method或property等关键字。
  • concept在Nim中比interface更加灵活。concept可以用于限制多个类型的行为,但不一定需要显式声明实现。它允许在函数中使用未显式声明为实现concept的类型。interface在C#中更加严格,要求类型明确地声明并实现接口中的所有成员。
  • 在C#中,interface可以在字段、方法参数、方法返回值等多个地方使用,允许动态地访问实现。在Nim中,concept主要用于参数约束,只允许在函数或过程中使用,无法在字段或返回值中声明

下面是这个案例中声明 Concept 的例子:

type
    IValueGetter = concept s
        s.getValue(int32) is int64

有了 concept,我们就可以在泛型中约束 泛型参数的行为,例如下面的泛型类型 MyTestClass[T] ,其类型声明中没有约束 T,但在相关的方法中,允许约束。

type
    MyTestClass[T] = object
        valueGetter: T

proc run[T: IValueGetter](this: MyTestClass[T]): int64 =
    var r = 0i64
    let n = high(int32) - rand(100).int32
    for i in 0 ..< n:
      r += this.valueGetter.getValue(i)
    return r

Nim 中 Struct 和 Class

在Nim语言中, struct 是使用 object 关键字,同样的,如果在object 的前面加上 ref 关键字,就变成了 struct 引用,自然就相当于 class 了,下面是示例代码:

type
    StructValueGetter = object
    StructValueGetter2 = object
        someField: int

    ValueGetter = ref object of RootObj
    ClassValueGetter1 = ref object of ValueGetter
    ClassValueGetter2 = ref object of ValueGetter

在这个例子中,我们看见如何声明 Struct,以及声明字段, 同时也使用 ref object 声明了 class ,他使用 of 关键字表示继承关系。

你也许注意到,RootObj ,他是nim语言提供的默认基类,你可以理解为c#和java 的object 基类,但nim语言具有很强的灵活性,可以不继承任何基类,或使用其他的基类。

Nim中定义方法或动态方法

一般情况下,Nim建议使用 proc 和 func 作为方法前的关键字,因为这样的话方法就是静态编译的,具有最佳的性能,func 其实是 proc 的特例,即表示无副作用的 proc,比如你仅仅是读取对象的数据,不会破坏结构,这就是无副作用。

# 实现 IValueGetter 的 concept
func getValue(this: StructValueGetter, index: int32): int64 =
    result = index.int64 + 3i64

func getValue(this: StructValueGetter2, index: int32): int64 =
    result = index.int64 + 5i64

func getValue(this: ClassValueGetter1, index: int32): int64 =
    result = index.int64 + 5i64

func getValue(this: ClassValueGetter2, index: int32): int64 =
    result = index.int64 + 7i64

为实现诸如 c# 的虚方法功能(即动态调用),nim 设计了 method 关键字。

method getValueCore(this: ValueGetter, index: int32): int64 {.base.} =
    quit "to overrdie"

method getValueCore(this: ClassValueGetter1, index: int32): int64 =
    return getValue(this,index)

method getValueCore(this: ClassValueGetter2, index: int32): int64 =
    return getValue(this,index)

# 为 ValueGetter 实现契约,相当于使用契约调用的是虚方法。
proc getValue(this: ValueGetter, index: int32): int64 =
    return getValueCore(this, index)

测试

下面我们将使用 stuct 和 class 两种形式,确定 concept 和 泛型结合,对性能的影响。同时我们也观察动态方法调用,即虚方法的调用对性能的影响。

import std/[times],std/random
type
    IValueGetter = concept s
        s.getValue(int32) is int64

    MyTestClass[T] = object
        valueGetter: T

proc run[T: IValueGetter](this: MyTestClass[T]): int64 =
    var r = 0i64
    let n = high(int32) - rand(100).int32
    for i in 0 ..< n:
      r += this.valueGetter.getValue(i)
    return r

type
    StructValueGetter = object
    StructValueGetter2 = object
        someField: int

    ValueGetter = ref object of RootObj
    ClassValueGetter1 = ref object of ValueGetter
    ClassValueGetter2 = ref object of ValueGetter

# 实现 IValueGetter 的 concept
func getValue(this: StructValueGetter, index: int32): int64 =
    result = index.int64 + 3i64

func getValue(this: StructValueGetter2, index: int32): int64 =
    result = index.int64 + 5i64

func getValue(this: ClassValueGetter1, index: int32): int64 =
    result = index.int64 + 5i64

func getValue(this: ClassValueGetter2, index: int32): int64 =
    result = index.int64 + 7i64

# 为接口 ValueGetter 实现契约,相当于使用契约调用的是委托。
method getValueCore(this: ValueGetter, index: int32): int64 {.base.} =
    quit "to overrdie"

method getValueCore(this: ClassValueGetter1, index: int32): int64 =
    return getValue(this,index)

method getValueCore(this: ClassValueGetter2, index: int32): int64 =
    return getValue(this,index)

proc getValue(this: ValueGetter, index: int32): int64 =
    return getValueCore(this, index)

proc measureTime(caption: string, procToMeasure: proc(): int64) =
  var startTime = cpuTime()
  let r = procToMeasure()
  var endTime = cpuTime()
  echo caption, " time = ", endTime - startTime, " result = ", r

# 运行测试
proc main() =
    randomize()
    let t1 = MyTestClass[StructValueGetter](valueGetter : StructValueGetter())
    measureTime("StructValueGetter ", proc ():int64 = t1.run() )
    let t2 = MyTestClass[ClassValueGetter1](valueGetter : new(ClassValueGetter1))
    measureTime("ClassValueGetter1 ", proc ():int64 = t2.run() )
    let t3 = MyTestClass[ClassValueGetter2](valueGetter : new(ClassValueGetter2))
    measureTime("ClassValueGetter2 ", proc ():int64 = t3.run())
    let t4 = MyTestClass[ValueGetter](valueGetter : new(ClassValueGetter1))
    measureTime("IValueGetter-1    ", proc ():int64 = t4.run())

    let t5 = MyTestClass[ClassValueGetter1](valueGetter : new(ClassValueGetter1))
    measureTime("ClassValueGetter1 ", proc ():int64 = t5.run())
    let t6 = MyTestClass[StructValueGetter2](valueGetter : StructValueGetter2())
    measureTime("StructValueGetter2", proc ():int64 = t6.run())


when isMainModule:
  main()

编译的命令行如下:

nim c -d:release -x --checks:off  -r ".\demo1.nim"

请自己编译时将 demo1.nim 替换成自己的文件名,同时你也可能注意到 我关闭了检查,我发现如果不关闭的话,性能会慢很多。在我的笔记本电脑的参考结果如下:

StructValueGetter  time = 0.429 result = 2305842997402533900
ClassValueGetter1  time = 0.421 result = 2305842825603845693
ClassValueGetter2  time = 0.421 result = 2305842971632730244
IValueGetter-1     time = 4.74 result = 2305842913650672596
ClassValueGetter1  time = 0.4210000000000003 result = 2305842870701000726
StructValueGetter2 time = 0.4190000000000005 result = 2305842920093123411

我们注意到:

  • 在nim语言实现中 , struct 和 class 在泛型和 concept 的环境下,性能几乎没有影响;
  • 与c#不同,nim仍然为每种 class 编译不同的程序,所以性能不会变化;
  • 尽量避免使用虚方法,性能的损失很大;

标签:index,Concept,int32,性能,Nim,object,getValue,int64,proc
From: https://www.cnblogs.com/tansm/p/2023-12-05-Nim-Concept-Performance.html

相关文章

  • Unity DOTS系列之托管/非托管Component的区别与性能分析
    最近DOTS发布了正式的版本,我们来分享一下DOTS里面托管与非托管Component的区别与性能分析,方便大家上手学习掌握UnityDOTS开发。托管与非托管的区别在于是不是基于自动垃圾回收的。托管是由垃圾回收器来负责自动回收,非托管需要我们手动来做相关内存管理,不被垃圾回收系统来处理。......
  • 小程序性能优化:深入探讨启动耗时的调优策略
    在移动应用开发中,用户对于程序启动速度的要求越来越高。在小程序开发中,启动耗时是一个关键指标,直接影响用户体验。本文将深入探讨小程序启动耗时的优化方法,并提供详细的代码演示,以帮助开发者提高小程序的性能。1.启动耗时的定义启动耗时是指从用户点击小程序图标到小程序完全加载......
  • 如何量化shader的性能标准学习心得
    序言,开头耳  随着如今手游、主机游戏的开发越来越重度。硬件性能跟不上效果产生的能耗,开发者需要主动关注shader的性能问题,并合理指定标准。标准的制定前首先是让问题可被量化。  参考外网大佬的文章:https://thegamedev.guru/unity-gpu-performance/shader-cost-analysis-m......
  • MKL.NET:为.NET开发者提供高性能数学计算支持的开源库
    MKL.NET:为.NET开发者提供高性能数学计算支持的开源库编程乐趣​ ​关注他 你经常看TA的内容MKL是英特尔推出的一套功能强大、性能优化的数学库,主要是采用C/C++编写的。今天给大家推荐一个MKL的.Net版本,让我们无需与C/C++打交道,方便我们集成到应......
  • SQLServer 性能报表的学习与使用
    SQLServer性能报表的学习与使用背景前面连续学习了SQLServer如何优化等事宜.但是一开始总是么有找到对应的问题解决思路周天时想到了SQLSERVER的MDW工具但是并没有找到一个合适的处理方式和方法.今天突然发现可以使用部分核心报表功能进行问题定位.所以想着总结一下,......
  • [LeetCode] 1266. Minimum Time Visiting All Points
    Ona2Dplane,therearenpointswithintegercoordinatespoints[i]=[xi,yi].Returntheminimumtimeinsecondstovisitallthepointsintheordergivenbypoints.Youcanmoveaccordingtotheserules:In1second,youcaneither:moveverticallyb......
  • TPC-DS工具介绍及性能测试
    一. Hive-testbench工具介绍TPC-DS:https://www.cnblogs.com/webDepOfQWS/p/10544528.html由于原生态工具生产测试数据表存在bug,后续引进hive-testbench,可参考:https://bbs.huaweicloud.com/blogs/260840可在WPS共享文档https://yundoc.fin-shine.com/drive/group/442074/325659......
  • Span<T> 的性能优势
     内存效率: 由于Span<T>可以直接引用内存中的数据,无需额外的内存分配,因此它在内存效率上非常高。减少垃圾回收: 由于不需要创建临时对象,Span<T>可以显著减少垃圾回收的压力,尤其对于大型数据集来说。并行性: Span<T>可以在多线程环境下高效地进行操作,因为它可以避免数据竞......
  • 三丰免费云服务器性能评价
    三丰免费云服务器是一种提供免费使用的云计算服务,它具有一定的性能和功能。以下是对三丰免费云服务器性能的评价:首先,三丰免费云服务器提供了可靠的计算性能。它采用了先进的硬件设备和虚拟化技术,能够提供稳定的计算资源。用户可以在云服务器上运行各种应用程序和服务,包括网站托管......
  • SQLServer性能优化之二
    SQLServer性能优化之二背景优化了机器的硬件配置之后性能好了很多但是偶尔还是会出现阻塞.SQL总是奇奇怪怪的.其实第一天时就感觉可能是索引存在问题.但是dbcc重建所有数据库的索引太慢了.所以作罢了,从HDD传输到SSD后大部分功能已经可以用了以为问题就此解决,但是......