是否值得从Python切换到Nim以提高性能?

Nim是Python语法和C性能的结合,







几周前,我在浏览GitHub时遇到了一个奇怪的存储库:该项目完全是用Nim编写的在那之前,我没有遇到过他,这次我决定弄清楚那是哪种动物。



起初我以为我落后于时代,这是许多与我不同的人积极使用的通用编程语言之一。然后我决定研究它。



这是我的结论:



  • 实际上,这种语言在狭窄的人群中很流行。
  • 也许应该是这样。


因此,我将向您介绍一些有关Nim的经验,简要介绍其中的编程功能,并尝试将其与Python和C进行比较。展望未来,我注意到这种语言对我来说非常有前途。



在工作室编码!



例如,我决定在Nim中写一些比世界打招呼更复杂的东西:







似乎没有什么多余的,对吧?看起来是如此简单,即使您以前从未听说过Nim,也可以轻松找出它的作用。(程序将输出:“ num:5 i:5”)



因此,让我们从某个地方分析一下我们似乎熟悉的东西。



变量声明



这对JavaScript开发人员来说是非常痛苦的。虽然有些语言使用var,有些语言使用let,但是JS和Nim允许您在声明变量时同时使用这两种语言。但是,需要注意的是,它们在Nim中的工作方式不同于在JS中的工作方式。但是稍后会更多。



积木



为了表示Nim中的新块,我们使用冒号后接缩进线。一切都像在Python中一样。



关键词



循环和if语句看起来都像是一段Python代码。实际上,从第5行开始的所有内容都是Python代码(假设我们定义了echo函数)。



因此,可以,Nim中也可以使用许多Python关键字和运算符:not,is,and,or等等。



也就是说,到目前为止,我们没有在Nim中看到什么特别的东西:Python的最差版本(就

语法而言),考虑到需要使用let或var声明变量的事实。



我们可以在这里停下来,但是有一个很大的“但是”:Nim是一种静态类型的语言,其运行速度几乎与C语言一样快,



好了,现在再进行一次对话。让我们来看看。



性能测试







在深入探讨Nim的语法(尤其是到目前为止尚未看到的静态类型的部分)之前,让我们尝试评估其性能。为此,我编写了一个简单的实现来计算Nim,Python和C中的第n个斐波那契数。



为了公平起见,我基于Leetcode解决方案(选项1)对实现进行了标准化,并尝试在所有三种语言中尽可能严格地坚持下去。



您当然可以提醒我有关LRU Cache的信息但是目前,我的任务是使用标准方法,而不是尝试优化计算。所以我选择了一个简单的实现。


以下是计算第40个斐波纳契数的结果:







是的,严格来说,不能将实验称为“干净”,但这与进行更认真测试的其他发烧友的结果相关[1] [2] [3]



我为本文编写的所有代码都可以在GitHub上找到,包括有关如何运行此实验的说明。



那么,为什么Nim比Python快得多?



好吧,我要说有两个主要原因:



  1. Nim是一种编译语言,而Python是一种解释语言(更多内容请参见)。这意味着在运行Python程序时需要做更多的工作,因为必须先解释该程序,然后才能运行。这通常会使语言变慢。
  2. Nim是静态类型的。尽管在我之前显示的示例中没有类型声明,但稍后我们将看到它确实是静态类型的语言。对于动态类型的Python,解释器还有很多工作要做,以适当地定义和处理类型。它还会降低性能。


工作速度增加-编码速度降低



这是Python Docs关于解释语言的说法:

« / , , ».


例如,这很好地概括了Python和C之间的折衷。您可以在Python中做的任何事情都可以在C中做,但是C程序将运行许多倍。



但是您将花费更多的时间编写和调试C代码,因为它笨拙且可读性差。这就是为什么不再需要C且Python受欢迎的原因。换句话说,Python要简单得多(当然,是相比而言)。



因此,如果Python在光谱的一端,而C在光谱的另一端,则Nim试图在中间找到某个位置。它比Python快得多,但编程起来却不像C难。



让我们看一下计算斐波纳契数的实现。



从:



#include <stdio.h>
int fibonacci(int n) {
    if (n <= 1) {
        return n;
    } 
    return fibonacci(n-1) + fibonacci(n-2);
}

int main(void) {
    printf("%i", fibonacci(40));
}


蟒蛇:



def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(40))


尼姆:



proc fibonacci(n: int): int = 
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

echo(fibonacci(40))


尽管Nim在其过程(函数)语法中使用“ =”符号,但总的来说,编写代码比使用C语言要容易得多。



也许这确实是一个值得权衡的选择吗?比Python难写一点,但是它的运行速度快十倍。我可以忍受。



Nim语法



import strformat

#    https://nim-lang.org/

type
  Person = object
    name: string
    age: Natural #      

let people = [
  Person(name: "John", age: 45),
  Person(name: "Kate", age: 30)
]

for person in people:

  echo(fmt"{person.name} is {person.age} years old")


我只是指出关键功能。



变数



我们使用var,let或const声明变量。



var和const的工作方式与JavaScript中的工作方式相同,但是让我们有一个不同的故事。



JavaScript let在范围方面与var不同,Nim let表示一个变量,其值在初始化后不能更改。在我看来,它就像Swift。



但这不等于常量吗?- 你问。



没有。在Nim中,const和let之间的区别如下:

对于const,编译器必须能够在编译时确定该值,而对于let,则可以在运行时确定该值。


文档中的示例:



const input = readLine(stdin) # Error: constant expression expected
let input = readLine(stdin)   #  


另外,可以像这样声明和初始化变量:



var
   a = 1
   b = 2
   c = 3
   x, y = 10 #   x  y   10


功能



Nim中的函数称为过程:



proc procedureName(parameterName: parameterType):returnType =
   return returnVar


鉴于该语言在许多方面与Python相似,因此当您初次看到它们时,这些过程似乎有些奇怪。



使用“ =”而不是“ {”或“:”显然会造成混淆。在同一行中编写过程,一切都会好一点:



proc hello(s: string) = echo s


您还可以获得函数的结果:



proc toString(x: int): string =
   result =
       if x < 0: “negative”
       elif x > 0: “positive”
       else: “zero”


感觉您仍然需要以某种方式返回结果,但是在这种情况下,结果不是变量-它是关键字。因此,从Nim的角度来看,上述代码段是正确的。



您还可以重载过程:




proc toString(x: int): string =   
    result =     
        if x < 0: "negative"     
        elif x > 0: "positive"     
        else: "zero"  
proc toString(x: bool): string =   
    result =     
        if x: "yep"     
        else: "nope"
echo toString(true) #  "yep"
echo toString(5) #  "positive"


条件和周期



它与Python有很多关系。



# if true:

# while true:

# for num in nums:


例如,要遍历列表,可以使用countup(开始,完成)countdown(开始,完成)来代替range(您可以更轻松地完成它,并在开始时将其用于i..finish



用户输入和输出



let input = readLine(stdin)
echo input


与Python相比,readLine(stdin)等效于input(),echo等效于print。



echo可以带或不带括号。



我的目标是使您对Nim有了基本的了解,而不是重述其所有文档。因此,我结束了语法,然后继续讲其他语言功能。



额外功能



面向对象编程



Nim不是一种面向对象的语言,但确实为使用对象提供了最少的支持当然,他远离Python类。



巨集



Nim支持宏和元编程,并且开发人员似乎对此非常重视。这是三个课程系列中其自身部分的主题



小例子:



import macros  macro myMacro(arg: static[int]): untyped =  
   echo arg

myMacro(1 + 2 * 3)


基本数据类型



string, char, bool, int, uint  float.


您还可以使用以下类型:



int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64


另外,与Python不同,Nim中的字符串是可变类型。



评论



与Python不同,Nim使用“#”字符和“ [”和“]”组合来进行多行注释。



# a comment#[
a
multi
line
comment
]#


编译JavaScript



Nim可以将其代码转换为JavaScript。不知道是否有很多人来使用它。但是有一个用Nim编写Snake浏览器游戏的例子



迭代器



Nim迭代器更像Python生成器:



iterator countup(a, b: int): int =
   var res = a
   while res <= b:
       yield res
       inc(res)




区分大小写 和下划线Nim仅对第一个字符区分大小写。



也就是说,他区分HelloWorld和helloWorld,但不区分helloWorld,helloworld和hello_world。因此,例如,以下过程将正常工作:



proc my_func(s: string) =
   echo myFunc("hello")


尼姆有多受欢迎?







Nim在GitHub上拥有近10,000个星星。这是一个明显的优点。不过,我尝试从其他来源估算该语言的普及程度,当然,它的普及程度还不高。



例如,在2020 Stack Overflow Survey中甚至没有提到Nim 我在LinkedIn上找不到Nim开发人员的工作(即使在全球范围内也是如此),并且在Stack Overflow上搜索[nim-lang]标签仅返回了349个问题(对于Python来说,这大约为1,500,000,而对于Swift则为270,000)



。因此,可以假设大多数开发人员都没有使用过它,并且许多人从未听说过Nim语言,这是公平的。



取代Python?



老实说,我认为Nim是很酷的语言。为了撰写本文,我研究了所需的最低要求,但这足够了。尽管我还没有对它进行深入的研究,但我计划将来使用Nim。就个人而言,我是Python的忠实拥护者,但我也喜欢静态类型的语言。因此,对我而言,在某些情况下,性能的提高可以弥补轻微的语法冗余。



虽然基本语法与Python非常相似,但更为复杂。因此,大多数Python爱好者可能对它不感兴趣。



另外,不要忘记Go语言。我相信你们中的许多人在阅读时都已经想到了这一点,这是正确的。尽管Nim的语法更接近于Python的语法,但就性能而言,它与诸如``简化的C ++''之类的语言精确竞争。

我曾经测试过Go的性能。特别是对于斐波那契(40),它的工作速度与C一样快。


但仍然:Nim可以与Python竞争吗?我非常怀疑。我们看到了提高计算机性能和简化编程的趋势。而且,正如我指出的那样,即使Nim提供了良好的语法/性能折衷,我认为击败纯粹和通用的Python还不够。

我与一位Nim Core开发人员进行了交谈。他认为Nim更适合从C ++迁移的人员,而不是python专家。


Nim可以与Go竞争吗?可能(如果Google允许)。Nim语言与Go一样强大。而且,Nim对C / C ++功能(包括宏和重载)有更好的支持。



但是,下次还会有更多关于此的信息。






广告



Epic服务器价格实惠的虚拟服务器,具有AMD处理器,CPU核心频率高达3.4 GHz。最大配置将使您充分利用-128个CPU内核,512 GB RAM,4000 GB NVMe 快点订购!






All Articles