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的程序也还能优化,可是结果应该不会有大的变化(因为相差太多)~~~

 

 

 

 

 

posted @ 2019-06-04 10:31  土豆008  阅读(725)  评论(0编辑  收藏  举报