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 编译不同的程序,所以性能不会变化;
  • 尽量避免使用虚方法,性能的损失很大;

Nim 概念 Concept 对性能的影响的更多相关文章

  1. [转帖]【译】RAID的概念和RAID对于SQL性能的影响

    [译]RAID的概念和RAID对于SQL性能的影响 https://www.cnblogs.com/VicLiu/p/11479427.html 简介 我们都听说过RAID,也经常作为SQL DBA. ...

  2. SQL Server中多表连接时驱动顺序对性能的影响

    本文出处:http://www.cnblogs.com/wy123/p/7106861.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...

  3. 极客时间 Mysql实战45讲 07讲行锁功过:怎么减少行锁对性能的影响笔记 极客时间

    极客时间 Mysql实战45讲 07讲行锁功过:怎么减少行锁对性能的影响笔记 极客时间极客时间 Mysql实战45讲 07讲行锁功过:怎么减少行锁对性能的影响笔记 极客时间 笔记体会: 方案一,事务相 ...

  4. 高性能JavaScript-JS脚本加载与执行对性能的影响

    在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 脚本位置对性能的影响 优化页面加载性能的原则之一是将scri ...

  5. JAVA 异常对于性能的影响

    陶炳哲 - MAY 12, 2015 在对OneAPM的客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代 ...

  6. HTTP/2 对 Web 性能的影响(下)

    一.前言 我们在 HTTP/2 对 Web 性能的影响(上)已经和大家分享了一些关于 Http2 的二项制帧.多用复路以及 APM 工具等,本文作为姊妹篇,主要从 http2 对 Web 性能的影响. ...

  7. smarty对网页性能的影响--开启opcache

    在上一篇<smarty对网页性能的影响>中,默认没有开启opcache,于是我安装了一下zend opcache扩展,重新实验了一下,结果如下: 有smarty 用apache的ab命令进 ...

  8. C++ 性能剖析 (四):Inheritance 对性能的影响

    (这个editor今天有毛病,把我的format全搞乱了,抱歉!) Inheritance 是OOP 的一个重要特征.虽然业界有许多同行不喜欢inheritance,但是正确地使用inheritanc ...

  9. css的!important规则对性能有影响吗

    最近在做项目中发现很多CSS代码里面都使用!important去覆盖原有高优先级的样式.按照常理来说,越是灵活的东西,需要做的工作就会更多.所以想当然的认为像!important这样灵活.方便的规则如 ...

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

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

随机推荐

  1. 更快更省更好用!天翼云云原生一体机iStack打通物云最后一公里!

    近年来,随着企业数字化转型的深入,从传统 IT 架构向云原生架构转型,已经成为企业谋求更高质量发展的必由之路.然而,云原生技术复杂度高,运维成本高,且技术工具间的集成度不足.打破云原生技术应用门槛,以 ...

  2. 耳分解、双极定向和 P9394 Solution

    耳分解 设无向图 \(G'(V',E')\subset G(V,E)\),简单路径或简单环 \(P:x_1\to \dots \to x_k\) 被称为 \(G\) 关于 \(G'\) 的耳,当且仅当 ...

  3. Flink 部署和整体架构

    一.Flink运行部署模式和流程 部署模式: 1.Local 本地部署,直接启动进程,适合调试使用 2.Standalone Cluster集群部署,flink自带集群模式 3.On Yarn 计算资 ...

  4. JavaDoc文档的介绍及生成方法

    javaDoc命令是用来生成自己的API文档的 参数信息 @author 作者名 @version 版本号 @since 指明需要最早使用的jdk版本 @param 参数名 @return 返回值情况 ...

  5. VS Code C++ 切换配置集

    前言 最近转型做Golang开发了,但有需求做视频传输,想用ffmpeg做测试,只是加点日志,方便测试,就想直接用VS Code做下开发好了,安装C/C++的插件,用MSYS2编译. 问题 C/C++ ...

  6. 普通人如何靠 AI 副业,1 个月实现月薪 3 万 +

    在物价飞涨.经济低迷的今天,仅靠死工资,却有着不固定的开销?房贷.车贷.孩子的教育费用-- 望着日益增长的开销,你是否也在夜深人静时,为钱包的羞涩而发愁?无数次幻想过拥有一份高收入的副业,却始终在迷茫 ...

  7. 使用QT开发远程linux服务器过程

    1.添加设备为通用linux 2.设置ip用户名 3.创建私钥文件,原来有的qtc那俩个文件删掉. 4.部署公钥,前提是测试链接要出现成功 5.在kits里添加编译环境设置编译器为32位或者64 6. ...

  8. Windows 提权-PrintNightmare

    本文通过 Google 翻译 PrintNightmare – Windows Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校正及个别注释补充 ...

  9. SQL注入之WAF绕过注入

    绕过WAF: WAF防御原理: 简单来说waf就是解析http请求,检测http请求中的参数是否存在恶意的攻击行为,如果请求中的参数和waf中的规则库所匹配,那么waf则判断此条请求为攻击行为并进行阻 ...

  10. hexo 图片添加水印(png, jpeg, jpg, gif)

    文章同步发布:https://blog.jijian.link/2020-04-21/hexo-watermark/ 本文折腾 hexo 图片添加水印功能,大部分代码沿用: nodejs 图片添加水印 ...