On Mar 22, 2013, at 6:32 AM, Tmusic wrote:

> I've been trying some things over the last couple days...
> 
> The pypy problem was indeed due to some external modules. Debug-pox.py does 
> not provide much helpfull information. Can I suggest adding a 
> traceback.print_exc() when import fails  ( +- line 80 in  boot.py [after: 
> print("Module not found:", base_name)]). In my case it really showed which 
> import failed.

This actually should happen.  I thought it was there with debug-pox.py, but can 
you try running pox.py --verbose and see if it gives a useful stack trace?

> For profiling I tried yappi (https://code.google.com/p/yappi/). Not as handy 
> as cProfile with RunSnakeRun, but it works with the threading model and 
> provides #calls, total time,... per function. It requires some changes in the 
> code, but it's possible to create some wrappers and load it as a POX module. 
> Let me know if you're interested in the code :)

Sounds interesting.  Do you have it in a github fork or anything?

> The second issue is the parsing of flow stats. When I'm getting a 
> flow_stat_reply from a switch I'm parsing the statistics for each flow 
> contained in the reply. It goes for up to about 300 flows, but from that 
> point links start disconnecting again. I tried to split the calculation into 
> different parts (don't process them in one loop, but fire an event for each 
> flow which will process only that flow). So far this had no measurable 
> impact. I'm guessing these events are processed right afterwards which 
> basically goes back to the "one big for loop" scenario. Can this be the case? 
> Pypy offers an improvement  of going up to about 550 flows, but then the same 
> issues arise again.
> 
> Further, I was looking at the recoco and revent libraries. What I'd like to 
> do is submit the "processing events" with a lower priority, so the packet in 
> events are processed first. I guess this can resolve the problem?
> Are there some features in recoco or revent which could help in implementing 
> this? When I print the length of the schedule queue (cycle function in 
> recoco), not all fired events seem to be scheduled as a separate tasks. Where 
> is the processing queue for the events situated?

Right, this would be my suggestion.  The OpenFlow event handlers are producers 
that fill a work queue and then you have a consumer in the form of a recoco 
Task that tries to drain the queue.

I think recoco could make this somewhat simpler than it is with just a little 
work, but I've so rarely hit performance problems that I've never fleshed it 
out.  In theory the recoco.events module might be a nice way to do this, but I 
think it's not as general purpose as it should be (and it has been a long time 
since I've used it at all).  I've thrown together a quick producer/consumer 
example using recoco:
  https://gist.github.com/MurphyMc/939fccd335fb3920f993

On my machine, run under CPython the consumer sometimes gets backlogged but 
eventually catches up, and on PyPy it pretty much stays caught up all the time. 
 In general, you'll need some application-specific logic for if/when the 
consumer gets really backed up (e.g., just throw away really old events, or 
don't yield and just churn through the backlog while stalling the other Tasks, 
or temporarily raise the consumer Task's priority, or etc.).  Or if the problem 
is that your production is just really bursty but not actually more than you 
can handle amortized over time then you can just ignore it as in the example.

Some of the things you can do to play with tuning the example are:
1. Adjust the consumer's priority.
2. Adjust the minimum number of items to consume (batch size) in one scheduling 
period (the min(10, ...) in run()).

In your case, I'd expect #1 might not do as much as you'd expect since it only 
matters when another Task -- e.g., the OpenFlow IO Task -- is actually waiting 
to run.  In your case, I'd expect the OpenFlow task to be mostly idle, but then 
you get a flow_stats and suddenly you have a lot of work to do.  #2 (or the 
equivalent) is probably more useful.  You want to set it high enough that 
you're not wasting time rescheduling constantly, but low enough that discovery 
doesn't get starved.

I think a lot of the time I get away with a much simpler approximation which is 
where events just update some state (e.g. counters, list of expired flows), and 
then I have a pending callDelayed which tries to process them, and then 
callDelayed()s itself again in some amount of time if there is nothing left to 
do and in some shorter amount of time if there is stuff left to do.

Another possibility may just be to adjust discovery's timeouts.  There's 
nothing magic about the defaults.

> And finally I noticed strongly varying performance (100 flows more or less in 
> the reply before it starts crashing) with exactly the same traffic patterns. 
> Has this something to do with the random generator in the recoco scheduler 
> cycle() function? 

Doubtful -- you probably don't have any Tasks now that have a priority other 
than 1, so the randomization shouldn't kick in.  My first guess is that this is 
nondeterminism caused by how Python 2.x is switching between the IO thread and 
the cooperative thread.  If you used the version of recoco from the debugger 
branch (which combines these into a single thread), you might find that it 
evens out.

-- Murphy

Reply via email to