[Ns-developers] finalizing the ns-3 object model (configuration)

Gustavo Carneiro gjcarneiro at gmail.com
Tue Jan 15 04:09:15 PST 2008


On 15/01/2008, craigdo at ee.washington.edu <craigdo at ee.washington.edu> wrote:
>
> Hi all,
>
> This is my $.02 on the topology and configuration sections of our
> "finalizing the ns-3 object model" discussion.
>
> I've been talking for some time about how hard it is to come up to speed
> on
> ns-3.  I've been commenting how sometimes it seems that everything in ns-3
> is done in some unusual or unexpected way.  I've used lots of terms to
> describe what we do in some cases and how to make things easier to deal
> with.  Well, some of you may not be surprised that I'm going to begin my
> comments on topology and configuration by introducing another new term
> (okay
> two terms):
>
> The Principle of Least Astonishment -- also known as the Rule of Least
> Surprise.  This was well put IMO by Raymond,
>
> "[ ... ] to design usable interfaces, it's best when possible not to
> design
> an entire new interface model. Novelty is a barrier to entry; it puts a
> learning burden on the user, so minimize it. Instead, think carefully
> about
> the experience and knowledge of your user base. Try to find functional
> similarities between your program and programs they are likely to already
> know about. Then mimic the relevant parts of the existing interfaces.
>
> "The Rule of Least Surprise should not be interpreted as a call for
> mechanical conservatism in design. Novelty raises the cost of a user's
> first
> few interactions with an interface, but poor design will make the
> interface
> needlessly painful forever. As in other sorts of design, rules are not a
> substitute for good taste and engineering judgment. Consider your
> tradeoffs
> carefully - and consider them from the user's point of view. The bias
> implied by the Rule of Least Surprise is a good one to hold consciously,
> mainly because interface designers (like other programmers) have an
> unconscious tendency to be too clever for the user's good."
>
> I'll also add a mantra that I spout quite often:  Make simple things
> simple
> and complex things possible.  I've also run across this phrased as, "make
> common things easy and rare things possible."
>
> So, I'm going to come at this in a relatively abstract way and focus on:
> novelty is a barrier to entry; interface designers have an unconscious
> tendency to be too clever for the user's good; poor design will make the
> interface needlessly painful forever; make common things easy and rare
> things possible.
>
> So, my first comment is, that when I saw:
>
>    Parameters params;
>    params.Set ("PointToPoint::ChannelDataRate", "5000000");
>    params.Set ("PointToPoint::ChannelDelay", "2");
>
>    Ptr<PointToPointChannel> channel0 =
>      PointToPointTopology::AddPointToPointLink ( n0, n2, params);
>
> I was astonished.  This code snippet seems to violate pretty much every
> usability guideline for interface design I've mentioned.  It's completely
> new and unique.  It makes a simple and common thing like passing 5000000
> to
> a function painful.  This pain will last forever.  I can see this kind of
> stuff appearing all over our codebase.  It does make rare things possible,
> though, and strikes me as way to clever.  It may work and it may enable
> some
> really hairy reconfiguration down at the low levels of the code, but I'll
> have a really, really hard time signing off on something like this.
>
> I think it may be time to take a step back and have a look at the issues.
>
> Tom identifies the following as important (most have been requirements for
> a
> year):
>
>    1. write general topology or scenario code and allow it to be run
>       populated with different types of objects, such as swapping out
>       TCP variants
>    2. expose existing parameter configurations
>    3. allow users to create new default variables for new classes
>    4. hook into command line arguments
>    5. support scope control
>
> It seems to me that there are two distinct things we've been talking
> about.
> There is a configuration subsystem and there is a replaceable
> component/topology subsystem.
>
> If you do break these two major classifications of functionality out, I
> think you can make a case for a simpler and easier to understand system.
>
> Configuration subsystems are common and known by lots of people.  Windows
> people know the registry.  Apple devotees know the System Configuration
> Framework.  Unix hackers understand .rc files.  Thoroughly modern Linux
> major-generals can deal with GConf.  There are lots of well-known and
> understood ways to configure complex systems.  If you believe in the
> principle of least surprise, we should configure ns-3 with something that
> looks, at least conceptually, like everything else that configures
> systems.
>
> I was thinking about this last Friday and it occurred to me that the
> namespace in our tracing system gets us almost there.  I have methods like
> the following scribbled on my whiteboard:
>
>   Set ("/nodes/*/tcp/reno/ssthresh", "16000");
>   Set ("/nodes/1/tcp/rajno/cwnd". "4000");
>
> I like this because it uses some of the same conceptual model that is
> required to use our tracing subsystem.  Perhaps tracing will be uncommon
> and
> unusual, but that's for a good reason; and once you get past that barrier,
> this will not be hard.  If you actually work with the tracing
> implementation, you could hook into the tracing callback mechanism to have
> objects notified of run-time changes to their configuration parameters
> (Mathieu has expressed interest in this).
>
> I can also see this as an *easy* way to get information out of the bowels
> of
> the system during or after simulations:
>
>   str = Get ("/nodes/1/tcpcraig/randomCounter");
>   NS_LOG_INFO ("the random counter ended up as " << str);


  Your proposal seems nice, but I would add:

  1- The ability to have named nodes.  Example:
       Get ("/nodes/AccessPoint1/tcpcraig/randomCounter");

  2- I think we should stop passing around values as strings: use the
correct C++ type instead for Get/Set.

  3- For language bindings it will be extremely useful that there exist
addition parallel APIs that work with type/value pairs instead of just
values, like "SetValue(std::string path, Ptr<const Value> value)", and
"Ptr<const Value> GetValue(std::string path)", where value is a class like:

class Value
{
   public:
      Ref ();
      Unref ();
      virtual Ptr<Value> Copy () const = 0;
      virtual std::string AsString () const = 0;
      virtual void SetFromString (std::string value) = 0;
};

(the type can be obtained by typeid, no need to be an explicit member)
and:

class BooleanValue
{
public:
  BooleanValue ();
  BooleanValue (bool value);
};

This will allow e.g. the python bindings to define generic type conversion
routines once (for each basic type) and call that conversion routine
everywhere the Value class is used.


This is, I think, quite nice from an API point of view.  An immediate issue
> that comes to mind is that the objects in question must exist in order to
> be
> configured in this way.  So there needs to be some kind of global
> configuration namespace:
>
>   Set ("/global/tcp/reno/ssthresh", "16000");
>
> This could be written by scripts before they started creating objects or
> given by command line.  It could conceivably be done by reading an xml
> file
> that has lines that look like:
>
>   <set key = "/global/tcp/reno/ssthresh", value = "16000"/>
>
> This is a natural division for scoping of configuration information; and
> notifications could be used to enforce finer granularity scoping.
>
> One more issue is how to add new configuration variables and get them
> documented.  This seems to me to be similar to how we get new tracing
> variables.  The difference is in the global case -- perhaps static
> variables
> -- needs to be investigated.  Perhaps there is one documentation solution
> for a unified tracing/configuration subsystem.
>
> This approach would also work for the simple case Tom mentions where you
> would want to substitute different kinds of "components" with identical
> (ideally default) constructors.  In this case you would need to describe
> some kind of name to the system (in the global namespace) that could be
> resolved into a typeid and constructed in the usual way.
>
> Now, maybe this is naive, and maybe I'm on crack, but something along
> these
> lines does seem to address the requirements and provide an interface that
> is
> not too surprising, is relatively easily understood by a user, and makes
> the
> simple and commonly done things relatively easy to do.
>
> What it does not address is the (possibly/perhaps probably?) rare, hard to
> do cases of generic topology.  My hope is that this could be best
> addressed
> with a rarely used, hard to understand API, leaving the basic stuff alone
> and easy to deal with.


Regarding all these topology issues, I must confess I am not too found of
the topology APIs.  Basically I am not too fond of too high lelvel APIs, and
in my simulations I prefer to just do my own for loops to create nodes etc.
If I were to think of an ideal "topology" API to use, it would a bit lower
level than what we have now.  I would welcome a set of APIs as building
blocks to greater things I'd do myself, like:

1- group = BeginGroup ()
2- for n in range(N) do
  2a- Create Node
  2b- Add a NetDevice to the node, given a technology name  (should match
the channel type)
  2c- Add the netdevice in 2b to the group
3- group.End ()
4- group.AddIpv4 ()

what 4. entails: 1. discover a unused IP subnet address;
2. for each node in group: a) add an IPv4 impl., b) re-add the netdevices to
the ipv4 impl. c) assign unique ipv4 address, inside the assigned subnet for
the group, for each netedevice;

2, 2a, 2b could optionally be simplified by something like
group.CreateNodes(int
numNodes, ClassId netdeviceType);

Maybe my BeginGroup () is just a shameless copy of some ideas I read today
regarding topology :)
In any case, I would point out some things the topology code got it wrong
IMHO:

  1- CsmaTopology and PointToPointTopology: why does topology have to be so
attached to particular L2 technologies?

  2- Need APIs to automatically assign IP subnets to groups, and unique IPs
in the subnets to nodes/netdevices

  3- Nodes could start as simple nodes first, upgraded in runtime to
"internet nodes" as needed; this will allow us to have topology APIs that
create topologies for L2 nodes, and the same API is useful for L3 nodes.

I wrote this email in a hurry, so if I'm on crack just let me know :-)

I think Mathieu's comments about data-flow versus control-flow are good.
> I've been pondering this control-flow problem off and on for some time.
> Hmmm.  Inversion-of-control-flow?
>
> Anyway, that's enough for this lengthy email.  Comments?
>
> -- Craig
>
>
>


-- 
Gustavo J. A. M. Carneiro
INESC Porto, Telecommunications and Multimedia Unit
"The universe is always one step beyond logic." -- Frank Herbert


More information about the Ns-developers mailing list