Asterisk Macro带来的性能较低问题

背景

Asterisk作为优秀的开源VOIP软件,其功能核心Dialplan辅助Asterisk核心完成一些复杂的业务功能。但其中一个常见的应用Macro在Dialplan的扩展中展现的相当灵活,方便用户快速扩展适合自己的应用。

Macro的灵活性也有它的弊端,即会导致较低的性能。

Macro原理

通过阅读app_macro代码发现Macro执行过程是递归调用。如:

[macro-1]
exten = s,1,Noop
exten = s,n,Macro(2)

[macro-2]
exten = s,1,Noop
exten = s,n,Macro(3)

[macro-3]
exten = s,1,Noop
exten = s,n,Macro(4)

[macro-4]
exten = s,1,Noop
exten = s,n,Noop()

在上述的dialplan中,macro-1将会递归调用macro-2,macro-3,直到macro-3返回后则此次调用结束,递归结束后需要按原路径回归,这样在回归阶段将会消耗一定的性能,从而造成实际性能的损失。

Macro核心框架

while (ast_exists_extension(chan, ast_channel_context(chan),
    ast_channel_exten(chan), ast_channel_priority(chan),
    S_COR(ast_channel_caller(chan)->id.number.valid,
    ast_channel_caller(chan)->id.number.str, NULL))) {
    /*!
     * 在此处开始调用macro-1 context下包含的extension,这样就会持续递归下去,
     * 除非遇到MacroExit时此次Macro才结束调用。
     */
    res = ast_spawn_extension(chan, ast_channel_context(chan),
        ast_channel_exten(chan), ast_channel_priority(chan),
        S_COR(ast_channel_caller(chan)->id.number.valid,
        ast_channel_caller(chan)->id.number.str, NULL),
			&foundx, 1);

    /*!
     * 此处extension执行完后检查chan是否已经挂断,挂断则进行栈回归阶段。
     */
    /* don't stop executing extensions when we're in "h" */
    if (ast_check_hangup(chan) && !inhangup) {
        ast_debug(1, "Extension %s, macroexten %s, priority %d returned normally even though call was hung up\n",
			ast_channel_exten(chan),
			ast_channel_macroexten(chan),
		    ast_channel_priority(chan));
	    goto out;
    }
}

Macro的替代品GoSub

Gosub摒弃了Macro的递归调用,而是构造了一个类似Linux栈的数据结构,这样后续的数据都会存放在这个栈上,当GoSub要结束时需要调用Return,然后回收资源。

GoSub实例

[from-internal]
exten => s,1,Noop()
exten => s,n,GoSub(from-internal-1,s,1(此处为传递给from-internal-1context的参数))

[from-internal-1]
exten => s,1,Noop()
exten => s,n,Return()

递归

递归的实现是函数通过直接或间接地调用自己,而函数调用时每次调用都要做地址保存,参数传递等,这是通过一个递归工作栈实现的。而调用结束时需要按原路返回(即回归阶段)以释放资源,这期间势必有大量重复的计算。

递归实现优缺点

  • 优点
    • 代码简洁清晰,容易验证正确性
  • 缺点
    • 耗费栈内存较多(有可能会出现递归栈溢出)(如:参数传递需要压栈等操作,会对执行效率有一定的影响
    • 大量重复计算

递归和循环执行效率对比

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/time.h>

static int count = 0;
static int total = 1000000;

static int function()
{
    if (count >= total) {
        return 0;
    }

    count++;
    function();
}

static int function2()
{
    for (int i = 0; i < total; i++) {
        count++;
    }
}

int main(int argc, const char *argv[])
{
    struct timeval start_time = {0};
    struct timeval ended_time = {0};

    gettimeofday(&start_time, NULL);
    function();
    gettimeofday(&ended_time, NULL);
    long diff = ended_time.tv_sec * 1000 * 1000 + ended_time.tv_usec - start_time.tv_sec * 1000 * 1000 - start_time.tv_usec;
    printf("recursive function take time: %ld\n", diff);

    gettimeofday(&start_time, NULL);
    function2();
    gettimeofday(&ended_time, NULL);
    diff = ended_time.tv_sec * 1000 * 1000 + ended_time.tv_usec - start_time.tv_sec * 1000 * 1000 - start_time.tv_usec;
    printf("linear function take time: %ld\n", diff);

    return 0;
}

运行结果

recursive function take time: 20203
linear function take time: 1991

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值