The ONE使用笔记:每个updateInterval都做些什么(world.update)

The ONE仿真器进行一系列初始化之后,开始仿真,详情可参考之前博文《The ONE使用笔记:main函数剖析》。仿真的核心部分是world.update()函数,world.update负责每隔一段时间,处理所有事件,并且更新所有节点。本文讲解The ONE仿真器每隔一段时间都做些什么。

1. The ONE运行机制

The ONE是基于代理离散事件(agent-based discrete event)仿真器。把节点间连接建立UP或DOWN,消息产生、发送、接收都视为事件,The ONE进行一些初始化工作后,开始仿真。每隔一段间(通过在设置文件Scenario.updateInterval来设置),系统处理每一个到期的事件,并同时更新所有节点。

1.1 main函数

以批处理(batch mode)为例,main函数整个调用过程如下,详情参考博文《The ONE使用笔记:main函数剖析》:

main(String[] args)    //DTNSim.java 
    initSettings(confFiles, firstConfIndex);  //初始化设置,读入default_settings.txt
    new DTNSimTextUI().start();
        initModel();         //创建仿真模型
        runSim();            //开始仿真
            world.update();  //在每个updateInterval,处理所有到期事件

1.2 world.update

world.udate,每隔updateInterval处理所有到期事件,并更新所有节点。值得注意的是:每处理一个到期事件,都会更新所有节点。完了之后,处理节点的移动,再次更新所有节点。相关源代码如下:

//World.java
private List<EventQueue> eventQueues;  //事件队列链表,用链表将事件队列组织起来
public void update () {
    double runUntil = SimClock.getTime() + this.updateInterval;  //

    /* process all events that are due until next interval update */
    setNextEventQueue();  //找到一个事件队列,该事件队列含有本updateInterval可以处理的事件
    while (this.nextQueueEventTime <= runUntil) {
        simClock.setTime(this.nextQueueEventTime);
        ExternalEvent ee = this.nextEventQueue.nextEvent();  //取得事件
         ee.processEvent(this); //处理事件,见2
        updateHosts();         //update all hosts after every event,见3
        setNextEventQueue();   
    }

    moveHosts(this.updateInterval);  //Moves all hosts in the world for a given amount of time
    simClock.setTime(runUntil);
    updateHosts();
}

2. 处理事件

消息的创建、转发、接收,连接的建立、撤消都可以视为事件。值得注意的是:使用MessageEventGenerator自动创建消息,只有创建消息事件MessageCreateEventworld.updateprocessEvent被处理,消息转发、接收则是在相关路由器的update被处理。我用MessageEventGenerator产生消息,用静态移动模型(即导入外部数据集,如infocom06)来仿真,以下是world.update处理的部分事件:

CONN up @464.0 43<->8
CONN up @464.0 43<->17
CONN down @464.0 43<->17
CONN up @464.0 43<->23
CONN down @464.0 43<->23
CONN down @464.0 43<->44
CONN up @464.0 43<->53
CONN up @464.0 43<->57
CONN down @464.0 43<->61
MSG @464.0 M16 [8->1] size:1016 CREATE
CONN up @465.0 0<->5
CONN down @465.0 0<->5
CONN down @465.0 0<->6

可见,在我的例子中,world.update只需处理消息创建和连接建立、撤消事件。关于事件处理,可参考之前博文《The ONE使用笔记:深入源码理解消息创建过程》和《The ONE使用笔记:连接事件ConnectionEvent读取与处理》。

3. updateHosts

3.1 节点顺序

The ONE仿真器将所有节点组织成一个ArrayList,依次取出节点做更新。这里取出节点的顺序有两种:其一,按网络地址network address排序的;其二,随机顺序。默认是随机顺序,可在设置文件设置,相关源代码如下:

# 可在设置文件设置
Optimization.randomizeUpdateOrder = true

public static final boolean DEF_RANDOMIZE_UPDATES = true; //默认是随机顺序,World.java

随机的顺序取决于当前的仿真时钟,相关源代码如下:

//World.java
private List<DTNHost> hosts;   //indexed by their network address
private ArrayList<DTNHost> updateOrder; //randomized order

//对所有节点进行随机排序,World.java的updateHosts()
Random rng = new Random(SimClock.getIntTime());
Collections.shuffle(this.updateOrder, rng);

3.2 updateHosts

updateHosts根据节点是否随机顺序,依次更新每一个节点,简化后的源代码如下:

//World.java
private void updateHosts() {
    if (this.updateOrder == null) { //indexed by their network address
        for (int i=0, n = hosts.size(); i < n; i++) {
            hosts.get(i).update(simulateConnections);
        }
    } else { //random order
        for (int i=0, n = hosts.size(); i < n; i++) {
            this.updateOrder.get(i).update(simulateConnections);
        }
    }

    if (simulateConOnce && simulateConnections) {
        simulateConnections = false;
    }
}

simulateConnections用于标识网络层network layer是否需要被更新,simulateConOnce标识只更新一次,相关源代码如下:

//World.java
private boolean simulateConnections; //Should network layer be updated too

private boolean simulateConOnce;  //Should the connectivity simulation be stopped after one round
public static final String SIMULATE_CON_ONCE_S = "simulateConnectionsOnce";
simulateConOnce = s.getBoolean(SIMULATE_CON_ONCE_S, false); //默认是false,可以在设置文件更改:Optimization.simulateConnectionsOnce

4. DTNHost.update

节点的更新,先判断节点是否处于活动状态,若不是,断开与该节点建立的所有连接;接着,判断是否需要更新网络层;最后,调用路由协议的update。其源代码如下:

public void update(boolean simulateConnections) {
    if (!isRadioActive()) {  // Make sure inactive nodes don't have connections
        tearDownAllConnections();
        return;
    }

    if (simulateConnections) {
        for (NetworkInterface i : net) {
            i.update();
        }
    }
    this.router.update();
}

关于router.update,可以参考博文《The ONE使用笔记:深入源码理解消息转发过程》和《The ONE使用笔记:深入源码理解消息接收过程》。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

2 thoughts on “The ONE使用笔记:每个updateInterval都做些什么(world.update)

  • 2015年09月13日 星期日 at 06:12下午
    Permalink

    updateHosts();在world.update里有两处出现,一处在循环内,一处在循环外,我看了您和另外位大大的讨论这两个 updateHosts();的问题,有一个疑惑:如果去掉循环外的 updateHosts();会出问题吗?循环内只要有一消息来了都要做 updateHosts();循环外还有必要吗?谢谢博主指点:)

    Reply
    • 2015年09月13日 星期日 at 08:43下午
      Permalink

      去掉外部的updateHosts,对你仿真结果几乎没什么影响,因为updateInterval默认是0.1。实际上,The ONE这样频繁调用updateHosts,目的在于仿真现实网络,只要有消息,有邻居,信道空闲就充分使用。想像下极限的概念,只要updateHosts被调用得越频繁(可以理解为非常频繁地检查节点的状态),那么也就越能仿真现实网络了。

      Reply