erlang的“缺点”
erlang的优点
1、并发简单
2、热更新
3、易于学习
为什么要学erlang?
erlang底层实现是C
所有简单来说,如果一个很精通C的程序员(20年+)肯定是比一个常规的erlang程序员写出的代码效率更高。
其实这个对于底层基于C的大部分语言(JAVA等)都是一个道理,
可是为什么要学erlang呢,因为成本,一个精通C的程序员培养的成本太大(这也是为什么JAVA程序员很多^_^)
什么时候用erlang解决问题?
erlang擅长的是大计算,小消息
简单的说,如果一个逻辑比较复杂的功能,而且结果是成本较低(相比逻辑实现来说),就可以用erlang
如果逻辑比较简单或者结果和逻辑差不多,就不适合了
下面我们举个例子来证明这种“缺点”
比如现在我要求一个整数里面所有的质数(只能被1和自己整除的数,假设1和2都是不是质数)
首先我们用C实现,为了方便比较我们用erlang nif调用C(理论只用C更快)
代码如下
#include <stdbool.h> #include <math.h> #include "erl_nif.h" static bool isPrime(int i) { int j; int t = sqrt(i) + 1; for(j = 2; j <= t; ++j) { if(i % j == 0) return false; } return true; } static ERL_NIF_TERM findPrime(ErlNifEnv *env, int argc, ERL_NIF_TERM argv[]) { int n; if(!enif_get_int(env, argv[0], &n)) return enif_make_badarg(env); else { int i; ERL_NIF_TERM res = enif_make_list(env, 0); for(i = 2; i < n; ++i) { if(isPrime(i)) res = enif_make_list_cell(env, enif_make_int(env, i), res); } return res; } } static ErlNifFunc nif_funcs[] = { {"findPrime", 1, findPrime} }; ERL_NIF_INIT(etest001, nif_funcs, NULL, NULL, NULL, NULL)
其中erlang的调用如下,C编译完成的叫etest001_nif.so
load() -> erlang:load_nif("./etest001_nif", 0). findPrime(_N) -> io:format("this function is not defined!~n").
我们来统计一下运行时间
11> timer:tc(etest001, findPrime, [100000]).
{8994,
[99991,99989,99971,99961,99929,99923,99907,99901,99881,
99877,99871,99859,99839,99833,99829,99823,99817,99809,99793,
99787,99767,99761,99733,99721,99719,99713,99709|...]}
我们用erlang实现
myprime(N) -> myprime(N+1,3,[]). myprime(N, N, L) -> L; myprime(N, X, L) -> case isprime(X) of false -> myprime(N,X+1,L); true -> myprime(N,X+1,[X|L]) end. isprime(1) -> false; isprime(2) -> false; isprime(N) -> M = erlang:trunc(math:sqrt(N)) + 1, isprime(M,2,N). isprime(M, M, _) -> true; isprime(M, X, N) -> if N rem X == 0 -> false; true -> isprime(M, X+1, N) end.
我们来统计一下运行时间
12> timer:tc(etest001, myprime, [100000]).
{65929,
[99991,99989,99971,99961,99929,99923,99907,99901,99881,
99877,99871,99859,99839,99833,99829,99823,99817,99809,99793,
99787,99767,99761,99733,99721,99719,99713,99709|...]}
好,现在可以看出erlang使用的时间差不多是使用nif(C语言)的8倍,而且随着这个数的增加,这个倍数应该是越来越大
有人可能会说,erlang擅长的是并发,你这个没有用并发阿,当然不快,好我们用并发,因为要用并发,而且结果要显示一个,所以肯定要用消息,
我们下面用并发的试试
代码如下
myprime2(N) -> Pid = self(), _ = [spawn(fun() -> isprime2(Pid, X) end) || X <- lists:seq(1,N)], myprime_work(N, []). myprime_work(0, L) -> L; myprime_work(N, L) -> receive false -> myprime_work(N-1, L); X -> myprime_work(N-1, [X|L]) end. isprime2(Pid, 1) -> Pid ! false; isprime2(Pid, 2) -> Pid ! false; isprime2(Pid, N) -> M = erlang:trunc(math:sqrt(N)) + 1, isprime2(Pid, M, 2, N). isprime2(Pid, M, M, N) -> Pid ! N; isprime2(Pid, M, X, N) -> if N rem X == 0 -> Pid ! false; true -> isprime2(Pid,M, X+1, N) end.
我们还是看看结果
13> timer:tc(etest001, myprime2, [100000]).
{456058,
[99991,99989,99971,99961,99929,99923,99907,99901,99881,
99877,99871,99859,99839,99833,99829,99823,99817,99809,99793,
99787,99767,99761,99733,99721,99719,99713,99709|...]}
这个实现的居然比先前的还慢,是先前单进程的(erlang的)6倍左右,是nif(C语言)的48倍左右
8994 μs(nif,C) ,65929 μs(erlang,单进程),456058 μs(erlang,多进程)
主要原因消息的成本(消息比较多,一个进程一个消息,还有创建进程的成本)是比计算还高很多~~~
大家可以在自己的环境试试,erlang的程序也还能优化,可是结果应该不会有大的变化(因为相差太多)~~~