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 编程中,简单的派生关系可以使用 枚举类型来提高性能。

Nim 枚举类型 对性能的影响的更多相关文章

  1. (转)类(class)和结构(struct)的区别是什么?它们对性能有影响吗?.NET BCL里有哪些是类(结构),为什么它们不是结构(类)?在自定义类型时,您如何选择是类还是结构?

    转自:http://blog.csdn.net/lingxyd_0/article/details/8695747 类(class)和结构(struct)的区别是什么?它们对性能有影响吗?.NET B ...

  2. 一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述MySQL中索引类型对数据库的性能的影响 2.RDB和AOF机制 3.Redis的过期键的删除策略 4.Redis ...

  3. Enum(枚举类型)的基本应用

    一.前言 在我们日常的开发过程中,我们经常定义使用常量:在Effective Java建议用枚举来替换常量的使用,提高我们代码的质量,总结一下枚举定义常量的基本使用 二.枚举类型说明      1.枚 ...

  4. 【转】Android中的内存管理--不错不错,避免使用枚举类型

    原文网址:http://android-performance.com/android/2014/02/17/android-manage-memory.html 本文内容翻译自:http://dev ...

  5. List是线程安全的吗?如果不是该怎么办呢?安全的List对性能的影响有多大呢?

    测试条件: 开启2个并行执行任务,往同一个list对象写入值 测试代码: ; static List<int> list = new List<int>(); static v ...

  6. Effective Java 第三版——34. 使用枚举类型替代整型常量

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  7. 结构体struct、联合体union、枚举类型enum

    1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...

  8. Python 中的枚举类型~转

    Python 中的枚举类型 摘要: 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期.月份.状态等. 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表 ...

  9. 从一个int值显示相应枚举类型的名称或者描述

    我正在做一个出入库管理的简单项目,在Models里定义了这样的枚举类型 public enum InOrOut { [Description("出库")] Out = , [Des ...

  10. C语言--enum,typedef enum 枚举类型详解

    原文:http://z515256164.blog.163.com/blog/static/32443029201192182854300/ 有改动 C语言详解 - 枚举类型 注:以下全部代码的执行环 ...

随机推荐

  1. CVE-2020-0796 SMB远程代码执行漏洞复现

    前言: 这个windows的永恒之黑漏洞,不得不复现一下啦! 这个漏洞诸多大佬都已经复现了,现在跟随大佬的脚步,逐个复现一下: 可参考:https://www.adminxe.com/1220.htm ...

  2. dBeaver操作iotdb并实现导入和导出

    1.windows下操作iotdb,现在官网下载相关的iotdb包 官网地址:https://archive.apache.org/dist/iotdb/   一般建议下载 -all的  2.打开db ...

  3. 【测试】自定义配置 RocksDB 进行 YCSB 测试

    目录 简介 编译 RocksDB 编译 YCSB 修复报错 自定义配置 RocksDB 进行 YCSB 测试 参考资料 本文主要记录在利用 YCSB 使用配置文件测试 RocksDB 的过程中遇到的一 ...

  4. Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.so

    在安装Docker以后,执行命令出现错误. Got permission denied while trying to connect to the Docker daemon socket at u ...

  5. springboot整合feign的接口抽离

    前言 现在很多微服务框架使用feign来进行服务间的调用,需要在服务端和消费端两边分别对接口和请求返回实体进行编码,维护起来也比较麻烦.那有木有一种可能,只用服务端编写接口,客户端像本地方法一样调用, ...

  6. Solution -「POJ 1322」Chocolate

    Description Link. 包里有无穷多个巧克力,巧克力有 \(c\) 种颜色,每次从包里拿出不同颜色巧克力的概率都是相等的,桌面的巧克力不允许颜色相同,若某次拿出的巧克力与桌上的巧克力颜色相 ...

  7. k8s1.25版本上实践 Ingress-nginx

    背景: 领导要求的最新最新版本k8s...使用ingress-nginx 对外暴露内部服务 环境 节点 master worker 主机/ip calico-master01/192.168.195. ...

  8. lvm格式化挂载分区

    1.从物理磁盘创建lvm分区 物理磁盘 /dev/sdb 20G 2.使用fdisk工具创建lvm分区 3.修改默认的分区类型 4.查看新建的分区 5.创建物理卷pv 6.创建逻辑卷组vg,并查看详情 ...

  9. IDEA降低注解检测级别

    在 File | Settings | Editor | Inspections 选项中使用搜索功能找到 Autowiring for Bean Class,将 Severity 的级别由之前的 er ...

  10. SQL函数升序Asc,降序Desc使用总结

    关键字-升序Asc及降序Desc的使用语法 对某一结果集按列进行升序或降序排列即:结果集 Order by 列名/数字 Asc/Desc. 一.Asc,Desc排序讲以下5点 1.不写关键字Asc/D ...