首页 > 其他分享 >Nim 枚举类型 对性能的影响

Nim 枚举类型 对性能的影响

时间:2023-12-05 16:47:05浏览次数:31  
标签:index kind Nim 性能 NI64 枚举 result ValueGetter2

Nim 枚举类型 对性能的影响

继上一篇文章《Nim 概念 Concept 对性能的影响》后,我在想,既然 method 虚方法造成性能的影响很大,那么有没有更快的方法实现,当然是有的,那就是枚举类型。

Enum Type

与很多的新设计的一样,Nim语言也内置了枚举类型,比如下面的代码:

type
    ValueGetterKind = enum
        vkClass1,
        vkClass2
    ValueGetter2 = ref object
        sharedField : int32
        case kind:ValueGetterKind
        of vkClass1:someField1 : int32
        of vkClass2:someField2 : int32

正如你看到的,在Nim语言中,通过为类型增加一个枚举字段(这里是kind),就可以为不同的类型定义不同的字段了,当然,你也可以定义共享的字段(这里例子的 sharedField)。

官方的例子中,演示了更复杂的情况,比如 多个类型公用相同的字段,或者定义多个字段。

# This is an example how an abstract syntax tree could be modelled in Nim
type
  NodeKind = enum  # the different node types
    nkInt,          # a leaf with an integer value
    nkFloat,        # a leaf with a float value
    nkString,       # a leaf with a string value
    nkAdd,          # an addition
    nkSub,          # a subtraction
    nkIf            # an if statement
  Node = ref object
    case kind: NodeKind  # the `kind` field is the discriminator
    of nkInt: intVal: int
    of nkFloat: floatVal: float
    of nkString: strVal: string
    of nkAdd, nkSub:
      leftOp, rightOp: Node
    of nkIf:
      condition, thenPart, elsePart: Node

var n = Node(kind: nkFloat, floatVal: 1.0)
# the following statement raises an `FieldDefect` exception, because
# n.kind's value does not fit:
n.strVal = ""

性能对比

接着我们之前的例子,我们看看在泛型的情况下,调用枚举类型对性能究竟影响有多少。

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
    ValueGetterKind = enum
        vkClass1,
        vkClass2,
        #vkClass3,
    ValueGetter2 = ref object
        sharedField : int32
        case kind:ValueGetterKind
        of vkClass1:someField1 : int32
        of vkClass2:someField2 : int32
        #of vkClass3:someField3 : int32

func getValue(this: ValueGetter2, index: int32): int64 =
    #return index.int64 + 7i64
    case this.kind:
    of vkClass1: 
        result = index.int64 + 9i64
    of vkClass2:
        result = index.int64 + 11i64
    #of vkClass3:
    #    result = index.int64 + 17i64

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 t7 = MyTestClass[ValueGetter2](valueGetter : ValueGetter2(kind:vkClass1, someField1:9))
    measureTime("ValueGetter2:1    ", proc ():int64 = t7.run())
    let t8 = MyTestClass[ValueGetter2](valueGetter : ValueGetter2(kind:vkClass2, someField2:11))
    measureTime("ValueGetter2:2    ", proc ():int64 = t8.run())
    #let t9 = MyTestClass[ValueGetter2](valueGetter : ValueGetter2(kind:vkClass3, someField3:11))
    #measureTime("ValueGetter2:3    ", proc ():int64 = t9.run())   

when isMainModule:
  main()

在我的机器中,测试结果如下(均使用release编译,下同):

ValueGetter2:1     time = 0.725 result = 2305842980222664759
ValueGetter2:2     time = 1.967 result = 2305842982370148375

我们看到,如果是判断分支的第一个,性能与本地调用稍差(0.7对比0.4),但与虚方法的5秒已经好太多。

细心的你,可能已经注意到我给的代码注释掉了第三种类型vkClass3,那么是不是作为分支的第三种情况,会更慢?让我们取消几个分支的注释,看看结果:

ValueGetter2:1     time = 1.300 result = 2305842894323320179
ValueGetter2:2     time = 1.345 result = 2305842842783714180
ValueGetter2:3     time = 1.295 result = 2305842993107566484

意不意外?第一个分支竟然变慢了,而后两个分支变快了,最后一个分支还稍稍快一点点。是否说明nim语言编译为2个枚举类型和3个枚举类型,使用了不同的编译代码? 让我们从nim生成的c语言源码验证这件事。

// 这是2个枚举的代码
N_LIB_PRIVATE N_NIMCALL(NI64, getValue__demo50_u28)(tyObject_ValueGetter2colonObjectType___liWfIs6eMPnbT0BuR4LSgg* this_p0, NI32 index_p1) {
	NI64 result;
	result = (NI64)0;
	switch ((*this_p0).kind) {
	case ((tyEnum_ValueGetterKind__tsIJjFnfs8zo9caQCLYJn1A)0):
	{
		result = (NI64)(((NI64) (index_p1)) + IL64(9));
	}
	break;
	case ((tyEnum_ValueGetterKind__tsIJjFnfs8zo9caQCLYJn1A)1):
	{
		result = (NI64)(((NI64) (index_p1)) + IL64(11));
	}
	break;
	}
	return result;
}
// 这是三个枚举的代码
N_LIB_PRIVATE N_NIMCALL(NI64, getValue__demo50_u30)(tyObject_ValueGetter2colonObjectType___liWfIs6eMPnbT0BuR4LSgg* this_p0, NI32 index_p1) {
	NI64 result;
	result = (NI64)0;
	switch ((*this_p0).kind) {
	case ((tyEnum_ValueGetterKind__tsIJjFnfs8zo9caQCLYJn1A)0):
	{
		result = (NI64)(((NI64) (index_p1)) + IL64(9));
	}
	break;
	case ((tyEnum_ValueGetterKind__tsIJjFnfs8zo9caQCLYJn1A)1):
	{
		result = (NI64)(((NI64) (index_p1)) + IL64(11));
	}
	break;
	case ((tyEnum_ValueGetterKind__tsIJjFnfs8zo9caQCLYJn1A)2):
	{
		result = (NI64)(((NI64) (index_p1)) + IL64(17));
	}
	break;
	}
	return result;
}

可以看出,代码完全是一样的,可能是编译器做了手脚。当然,我也测试了更多枚举的可能,比如7种,我发现所有分支时间都变长了(达到2秒),但总体是比虚方法好的。

共享的字段

在 getValue 方法中,如果你没有对类型进行判断,那么实际测试的成绩和静态方法调用是一样的,这可以被利用到我们面向对象编程中,经常用到的基类统一实现的场景。

总结

在 nim 编程中,简单的派生关系可以使用 枚举类型来提高性能。

标签:index,kind,Nim,性能,NI64,枚举,result,ValueGetter2
From: https://www.cnblogs.com/tansm/p/2023-12-05-Nim-Enum-Performance.html

相关文章

  • C/C++ 实现枚举网上邻居信息
    在Windows系统中,通过网络邻居可以方便地查看本地网络中的共享资源和计算机。通过使用WindowsAPI中的一些网络相关函数,我们可以实现枚举网络邻居信息的功能,获取连接到本地网络的其他计算机的相关信息。本文将介绍一个简单的C++程序,使用WindowsAPI枚举网络邻居信息,并获取对端名称......
  • .NET8 极致性能优化 AOT
    前言.NET8对于性能的优化是方方面面的,所以AOT预编译机器码也是不例外的。本篇来看下对于AOT的优化。概述首先要明确一个概念,.NET里面的AOT它是原生的。什么意思呢?也就是说通过ILC编译器(AOT编译器,参考:.Net7新编译器ILC简析)编译出来的代码是各个平台上可以直......
  • .NET8极致性能优化AOT
    前言.NET8对于性能的优化是方方面面的,所以AOT预编译机器码也是不例外的。本篇来看下对于AOT的优化。原文:.NET8极致性能优化AOT详述首先明确一个概念,.NET里面的AOT它是原生的。什么意思呢?也就是说通过ILC编译器(AOT编译器,参考:.Net7新编译器ILC简析)编译出来的代码是各个平......
  • 7、虚拟机性能监控、虚拟机快照以及宿主机上的虚拟机迁移到另一个宿主机
    摘自:https://blog.51cto.com/mfc001/6410333 虚拟机性能监控图形管理页面--Edit--preferences--polling--全部开启--close 关掉管理页面并重新virt-manager启动图形管理页面--View中选项全部开启   虚拟机快照点击虚拟机打开虚拟机页面--点击......
  • Nim 概念 Concept 对性能的影响
    Nim概念Concept对性能的影响继上一篇文章《C#泛型编译特性对性能的影响》后,我又研究了Nim语言相关的设计,由于Nim语言与C#语言有些差异,比如Nim没有接口,也没有直接的class关键字,所以某些实现是变通的办法。概念Concept在Nim中没有Interface的概念,虽然有多次提案,......
  • 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工具但是并没有找到一个合适的处理方式和方法.今天突然发现可以使用部分核心报表功能进行问题定位.所以想着总结一下,......