Nim 枚举类型 对性能的影响
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 枚举类型 对性能的影响的更多相关文章
- (转)类(class)和结构(struct)的区别是什么?它们对性能有影响吗?.NET BCL里有哪些是类(结构),为什么它们不是结构(类)?在自定义类型时,您如何选择是类还是结构?
转自:http://blog.csdn.net/lingxyd_0/article/details/8695747 类(class)和结构(struct)的区别是什么?它们对性能有影响吗?.NET B ...
- 一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1.简述MySQL中索引类型对数据库的性能的影响 2.RDB和AOF机制 3.Redis的过期键的删除策略 4.Redis ...
- Enum(枚举类型)的基本应用
一.前言 在我们日常的开发过程中,我们经常定义使用常量:在Effective Java建议用枚举来替换常量的使用,提高我们代码的质量,总结一下枚举定义常量的基本使用 二.枚举类型说明 1.枚 ...
- 【转】Android中的内存管理--不错不错,避免使用枚举类型
原文网址:http://android-performance.com/android/2014/02/17/android-manage-memory.html 本文内容翻译自:http://dev ...
- List是线程安全的吗?如果不是该怎么办呢?安全的List对性能的影响有多大呢?
测试条件: 开启2个并行执行任务,往同一个list对象写入值 测试代码: ; static List<int> list = new List<int>(); static v ...
- Effective Java 第三版——34. 使用枚举类型替代整型常量
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 结构体struct、联合体union、枚举类型enum
1.c语言中的类型 1)内置类型——char,short,int,float,double: 2)用户自定义类型(UDT)——struct结构体,union联合体,enum枚举类型 2.内存对齐 2. ...
- Python 中的枚举类型~转
Python 中的枚举类型 摘要: 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表示某些特定的有限集合,例如星期.月份.状态等. 枚举类型可以看作是一种标签或是一系列常量的集合,通常用于表 ...
- 从一个int值显示相应枚举类型的名称或者描述
我正在做一个出入库管理的简单项目,在Models里定义了这样的枚举类型 public enum InOrOut { [Description("出库")] Out = , [Des ...
- C语言--enum,typedef enum 枚举类型详解
原文:http://z515256164.blog.163.com/blog/static/32443029201192182854300/ 有改动 C语言详解 - 枚举类型 注:以下全部代码的执行环 ...
随机推荐
- 全局多项式(趋势面)与IDW逆距离加权插值:MATLAB代码
本文介绍基于MATLAB实现全局多项式插值法与逆距离加权法的空间插值的方法,并对不同插值方法结果加以对比分析. 目录 1 背景知识 2 实际操作部分 2.1 空间数据读取 2.2 异常数据剔除 2 ...
- ATtiny88初体验(七):TWI
ATtiny88初体验(七):TWI TWI模块介绍 ATtiny88的TWI模块兼容Phillips I2C以及SMBus,支持主从模式,支持7bit地址,最大允许128个不同的从机地址.在多主机模 ...
- 文心一言 VS 讯飞星火 VS chatgpt (95)-- 算法导论9.2 4题
四.用go语言,假设用RANDOMIZED-SELECT 去选择数组 A=(3,2,9,0,7,5,4,8,6,1)的最小元素,给出能够导致 RANDOMIZED-SELECT最坏情况发生的一个划分序 ...
- dedebiz 清理冗余废弃未引用图片方法
原理描述: 在原有织梦后台菜单中增加"清理冗余图片按钮",实现清理冗余图片的功能. 操作步骤: 1. 打开后台admin\sys_sql_query.php代码 在该文件中搜索如下 ...
- RK3568开发笔记(十一):开发版buildroot固件移植一个ffmpeg播放rtsp的播放器Demo
前言 目标开发任务还有个功能,就是播放rtsp摄像头,当然为了更好的坐这个个,我们必须支持rtsp播放失败之后重新尝试,比如5s重新尝试打开一次,从而保障联网后重新打开,然后达成这个功能. D ...
- webgl centroid质心插值的一点理解
质心插值说的是什么 2023.10.04再次review这个细节点: https://www.opengl.org/pipeline/article/vol003_6/ https://github. ...
- c语言代码练习1
//输出1-n阶乘的和,n为输入值//计算n的阶乘可以使用for循环,也可使用while循环#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h ...
- 详解RecyclerView的预布局
概述 RecyclerView 的预布局用于 Item 动画中,也叫做预测动画.其用于当 Item 项进行变化时执行的一次布局过程(如添加或删除 Item 项),使 ItemAnimator 体验更加 ...
- 这款 7k Star 的国产监控系统,真不错!
我们都知道天下没有"永不宕机"的系统,但每次线上出问题都要拉出一个程序员"祭天".所以一款靠谱.好用的监控工具就显得十分重要,它可以在生产环境出故障的第一时间发 ...
- FFMPEG+SDL简单视频播放器——视频快进
之前写过一篇关于视频播放器的文章.播放器只简单实现了视频播放的功能,在此功能的基础上,给它加上一个视频快进的功能. 实现 添加参数 // video play control bool do_seek ...