`
searun
  • 浏览: 174230 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
社区版块
存档分类
最新评论

在OMNeT++中如何有效地使用EV来输出

阅读更多

这是Andras发表的一篇在OMNeT++中如何使用EV来有效的输出日志信息的文章。原网址在http://www.omnetpp.org/article.php?story=20040804203301352

 

译者注:现在的 OMNeT++4.0 已经将这里的结果集成了进去,推荐使用 EV 来输出日志信息。具体的定义见 include/cenvir.h 文件。这篇文章详细的讨论了如何设置 EV ,具有普遍的借鉴作用。

 

ev<< 语句可以用来打印信息从而了解到仿真模型正在做什么。这在进行调试和理解模型运行的时候是很有用的。现在的问题是,当模型需要运行很长时间的时候, ev<< 语句将会消耗大量的 CPU 周期。这个时候该怎么办呢?

 

本文将介绍如何高效率的进行记录,并且同时介绍如何创建 log channels 或者调试 channels

 

Cmdevn 环境下,当设置 express-mode=true 的时候,输出将会被丢弃而不会被打印,从而使得执行可以更快。但是实际上, ev<< 语句还是会带来一些开销。这种速度的减小可能不会被注意到,但是影响却是很大的。这里的问题是, OMNeT++ 将只会丢弃也就是不打印已经转换为文本格式的字符串。举个例子,如下面的语句:

ev << "Average bit/sec is: " << totalBits/simTime() << "n";

 即使是在极速模式下, simTime() 将会被调用,浮点数除法也会被执行。结果将会被转换为字符串并存储在缓冲区中,而这里的缓冲区则会被丢弃。很遗憾的是, OMNeT++ 核心没有办法阻止这种事情发生,因为这是 C++ 的工作方式。

这时候该怎么办呢?一种常见的方式是采用 #ifdef

#ifdef DEBUGGING
ev << "Average bit/sec is: " << totalBits/simTime() << endl;
#endif

这并不坏,但是却有一个很严重的问题:在切换打印输出的时候必须重新编译所有的文件。根据墨菲定律(有可能出错的事情,就会出错,Anything that can go wrong will go wrong),当人们需要输出的时候往往看不到有输出。另外,代码中满含有 #ifdef 也不是一个好办法。随后想到的就是如何在编译的时候就将是否输出考虑到,下面是一个示例代码。

if (debugging)
    ev << "Average bit/sec is: " << totalBits/simTime() << endl;

这比前面要好一些。 if 语句的开销是比较小的。这样,就可以在初始化的时候通过 debugging 变量来决定是否输出信息。

debugging = par("debugging").boolValue();

  这种做法还不是很方便,因为我们需要手工维护输出的状态。有些人会发现此值可以在调试中进行动态设置,但是这还不能令人满意。为什么不能让代码知道我们是否需要记录呢?

实际上,我们可以回答这个问题。一般的, OMNeT++ 知道我们何时需要进行记录:不在极速模式下的时候。幸运的是, ev 对象可以知道这点,所以现在最新的代码如下。

if (!ev.disabled())
    ev << "Average bit/sec is: " << totalBits/simTime() << endl;

 

  几乎就是这样了。现在的问题是需要为每个 ev 输出增加一个 if 语句。有经验的 C/C++ 程序员将会马上想到采用宏来产生精炼的代码。第一次尝试:

#define EV   if (!ev.disabled()) ev    // *** DANGEROUS!***
...
EV << "Average bit/sec is: " << totalBits/simTime() << endl;

  注意,这里的宏将会产生很严重的问题。考虑一下下面的代码:

if (totalBits>1000)
    EV << "Average bit/sec is: " << totalBits/simTime() << "n";
else
    EV << "Not enough data yet" << endl;

  这段代码并不会按照设想的那样工作。当我们宏替换完成并重新缩进代码后,将得到下面的代码:

if (totalBits>1000)
    if (!ev.disabled())
        ev << "Average bit/sec is: " << totalBits/simTime() << endl;
    else if (!ev.disabled())
        ev << "Not enough data yet" << endl;

  所以这里的代码将永远不会打印出“ Not enough data ”。

 

最好是忘记上面的 EV 定义,因为这很容易会使得你栽在上面。即使是在这个宏定义中加上一对括号也不能解决问题,因为打印参数将会在括号之外。看起来这个问题没法修复。

尽管如此,让我们来看看下面这个版本:

#define EV   ev.disabled() ? ev : ev
...
EV << "Average bit/sec is: " << totalBits/simTime() << endl;

  这看起来有点奇怪。这里的宏定义看起来没有区别(无论是为 true 或者是 false ),而且这和所有 C 语言教科书中所倡导的(宏如果需要扩展成表达式需要加上圆括号)相违背。但是确实这个宏是可以工作的。现在 EV<< 将会变成一个简单的表达式。

 

ev.disabled() ? ev : ev << "Average bit/sec is: " << totalBits/simTime() << endl;

  这和下面的相同(注意符号的优先级) :

 

ev.disabled() ? ev : (ev << "Average bit/sec is: " << totalBits/simTime() << endl);

  这时候,当 ev 被禁止的时候(条件为 true ),这只是简单的一个 ev 对象的引用(这最终将会被编译器所优化,而不会产生任何的 CPU 指令);当 ev 启用的时候(条件为 false ),将会被还原成原始的 ev<< 语句。这正是我们所需要的。证明这里的 EV 定义在任何使用场景中都是可行的,这可以作为练习。无论是否有 if 语句,或者是还有一个 ?: 操作符,或者是其他的场景,这都是适用的。

实际上,上面 EV ?: 版本并不能在 VC++ 7.0 中通过编译(因为在 VC++ 7.0 中需要?:三元操作符的第二个和第三个参数的类型是一样的,而不会做默认的转换)。所以在 VC7 中的版本是这样的:

#define EV   ev.disabled() ? (std::ostream&)ev : ev

 (译者注:实际上,由于现在最新的 OMNeT++ 4.0 并不支持使用 VC 编译器进行编译,所以也没有采用这样的方式)。

 

如果你没有用过 log4j 或者是 C++ 中类似的工具( log4Cpp libCWD 等),那你有可能错过调试管道或者说是日志管道。简单地说, channels 是针对快速滚动日志问题的答案(因为你几乎不可能在日志的海洋中找到有用的信息)。你的代码日志将会记录到多个管道中,而在调试的时候可以只关注自己感兴趣的管道。有两个标准可以用来区分管道日志: topic 和调试级别(如 detail, info, warnings )。其中第三个标准是位置(模块位置),这已经被 OMNeT++ 内置了。可以通过查看模块的输出来得到你想要的信息。

一个比较好的消息是通过上面的 EV 定义,可以用来简单的模拟日志管道。当书写一个 IP 模块的时候,检查下面的定义:

#define fwdingEV   (ev.disabled()||!fwdingChannel) ? (std::ostream&)ev : ev
#define localEV    (ev.disabled()||!localChannel) ? (std::ostream&)ev : ev
#define mcastEV    (ev.disabled()||!mcastChannel) ? (std::ostream&)ev : ev
#define dropEV     (ev.disabled()||!dropChannel) ? (std::ostream&)ev : ev

上面的定义提供了四个日志信道( wdingEV, localEV, mcastEV, dropEV ),可以通过单独设置 fwdingChannel, localChannel, mcastChannel, dropChannel 布尔变量的值来进行开关。可以像下面这样使用日志管道。

...
EV << "packet received" << endl;
...
if (destAddress.isMulticast())
{
    mcastEV << "multicast packet, addr=" << destAddress << endl;
    ...
}
...
if (!routeFound)
{
    dropEV << "unroutable packet, dropping" << endl;
    delete datagram;
}
...
 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics