Home > Erlang探索 > 例证NIF使用的误区

例证NIF使用的误区

December 2nd, 2010

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: 例证NIF使用的误区

NIF是什么? A NIF library contains native implementation of some functions of an Erlang module.
不清楚的同学请参考http://www.erlang.org/doc/man/erl_nif.html.
这个功能对于扩展Erlang,利用现有的遗留c的财产,提高性能非常有帮助.
但是通常同学们会无视手册里面的一句话:

Avoid doing lengthy work in NIF calls as that may degrade the responsiveness of the VM. NIFs are called directly by the same scheduler thread that executed the calling Erlang code. The calling scheduler will thus be blocked from doing any other work until the NIF returns

导致了非常严重的设计问题. 比如在NIF里面调用mysql client api, 作费时的IO操作等等, 我已经看到好几个同学这么干了,为了揭示这个问题的严重性, davisp同学为我们写了个例子来演示这个问题: 代码在这里
https://github.com/davisp/sleepy
.

Sleepy – A misbehaving NIF
This demonstrates what happens if a NIF takes a long time and is called from as many schedulers as exist in the VM. Namely, that the VM is halted until the NIF functions return.

我们来演示下把:

#  git clone https://github.com/davisp/sleepy.git
git clone https://github.com/davisp/sleepy.git
Initialized empty Git repository in /tmp/sleepy/.git/
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (7/7), done.
# cd sleepy
## 这个davisp同学是个Mac控, 我们在 linux下工作的, 需要修改下Makefile
# diff Makefile Makefile.orig 
2c2
< INCLUDES = -I$(OTPROOT)/erts-5.7.5/include/
---
> INCLUDES = -I$(OTPROOT)/erts-5.8.2/include/
5c5
< GCCFLAGS = -O3 -fPIC -bundle -flat_namespace -undefined suppress -fno-common -Wall 
---
> #GCCFLAGS = -O3 -fPIC -bundle -flat_namespace -undefined suppress -fno-common -Wall 
8c8
< #GCCFLAGS = -O3 -fPIC -shared -fno-common -Wall
---
> GCCFLAGS = -O3 -fPIC -shared -fno-common -Wall

##david同学写的代码不够体贴人
# diff sleepy.erl  sleepy.erl.orig 
6c6
<     erlang:load_nif("./sleepy", 0).
---
>     erlang:load_nif("sleepy", 0).

# 好吧, 编译运行

# make run
erl -noshell -s lockvm lock -s init stop
Starting heartbeat.
Tick
Tick
Tick
Tick
Locking the VM 从这开始你的VM被堵死
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick
Tick

我们可以看到NIF在sleep, 堵死了所有的调度器, 你的erlang VM也在睡觉, zZzZZz, 好舒服哦, 你的系统性能就惨了.

结论: 避免在NIF里面作费时的操作.

Post Footer automatically generated by wp-posturl plugin for wordpress.

Categories: Erlang探索 Tags: ,
  1. December 4th, 2010 at 12:42 | #1

    这让我想起很多同学在他们自己的 nginx C 模块中直接调用阻塞式的 API (例如 libmysql 的),然后让 nginx 进程的并发能力变成 1,呵呵。。。

    其实究其主要原因,还是因为不少同学对 IO 复用的基本原理并不太清楚 😉

  2. ttylikl
    December 7th, 2010 at 23:10 | #2

    NIF的文档里内容并不多,只要细心看,一定可以在性能和并发的那段里看到提示的。总之就是要记住,NIF里必须做非常简单快速,不会导致资源竞争的操作。尽快的结束函数运行返回。:)

  3. shoumuyushan
    December 25th, 2012 at 21:45 | #3

    霸爷你好,我想问下,如果自己实现一个比较费时的bif,也会跟nif出现同样的情况么?

    Yu Feng Reply:

    bif有trap机制可以绕开这个问题,otp自己的费时的bif都是支持trap的,所以能实现公平调度。

    shoumuyushan Reply:

    谢谢,我再研究一下。
    另外,我发现,启动erl +s 30:30 ,只有一个进程调用sleepy:sleep()时,节点会失去响应。
    按照我粗浅的理解,应该只有一个scheduler在等待nif返回,为什么整个vm都在等待。

    另外,如果是这样的话,是不是意味着,我开smp的时候,用nif,比较不划算。

    shoumuyushan Reply:

    我在lockvm:tick()的时候把当前时间打印出来了,
    发现nif返回之后,瞬间出了一串同样的时间打印。
    理解不了。

Comments are closed.