Archive

Archive for September, 2008

SSPS, Python, and static arrays

September 29th, 2008 heckel No comments

I can build a convert function that turns the static arrays into lists, which then get passed out. The problems:

  • Whenever the field is accessed, it goes through the static array conversion
  • Pickle doesn’t “just work”
  • Pickle, in fact, has to go through another set of conversions and have setter methods as well, which it just doesn’t want to do

I’ve posted to the python-c++ list to ask if there is a Better Way. There are three options (a) there IS a Better Way (b) I manually extract the data from the objects once it’s in Python, building up native python objects or (c) rewrite SSPS in Python.

Given the current situation, I’m going to work on (b).

Categories: SSPS, Stupid Python Tricks Tags:

CPU Usage from Python in Windows

September 17th, 2008 heckel No comments

Using Boost to wrap C++ code for Python

September 13th, 2008 heckel No comments

Key items:

  • Add the path to the boost headers to project include path
  • Add the path to the python header to project include path
  • Add the path to the boost library binaries to project link path
  • Add the path to the python library binary to project link path
Categories: Stupid Python Tricks Tags:

AgentThread innards

September 10th, 2008 heckel No comments

I’ve started filling out the major components of the agent core; I have a base class for AgentBehaviors, am filling in functionality for the AgentThread, and have rough versions of the AgentController, GameController and main application threads. Most of the work I’ve done so far is on the AgentThread.

The AgentThread class has six major methods at present:

  • run() : The meat of the agentthread. It first sorts the behaviors by priority and builds the generator functions for each behavior. It then goes into its main loop; each iteration includes a subloop which iterates over each behavior as many times as it is able given available resources. At present, it depends on the AgentController to tell it when to stop, collect its answers, and  reset for the next run. At present, it is not guaranteed to run all behaviors in the list.
  • addBehavior(behavior,priority): Adds a behavior at a given priority — default is priority 0. Lower priorities run first.
  • stop(): Tells the thread to stop running completely
  • reset(): Tells the thread to collect its effector answers and reset for the next run. Also increments the step counter.
  • setPercepts(): Sets up percepts for next timestep. Should be called before reset()
  • getEffectors(): Gets the collected effector requests. Should be called after reset(). Returns false if effectors are not yet ready for harvest

These will change as the agentthread becomes more properly resource aware and is given a specific quantity of system time to work with. Also note that at present, the run thread starts working immediately after returning effector results; ultimately it will wait until the AgentController thread tells it to continue.

Categories: Agent Core, AgentThread Tags:

Python Logging

September 10th, 2008 heckel No comments

Here’s the doc page for the Python logging facility.

Categories: Stupid Python Tricks Tags:

Messing with OpenSteer

September 6th, 2008 heckel No comments

As there is no Python wrapper for OpenSteer, I started looking into what it will take to wrap it. There are several options for creating Python wrappers, notably SWIG and Boost. SWIG is automatic, but loses the class structure of the underlying code. Boost wrappers have to be hand-crafted, but I think it will be the better choice. Either way, I need to build the OpenSteer plugins first and then wrap them in Python. One useful aspect of OpenSteer is that I can use the OpenGL-based demo program for now until FI3RST is ready.

I have started messing with the CaptureTheFlag plugin included with the demoes. I’ve stripped out the pursuing agents, and created a Patrol plugin. This just generates four goal points which need to be patrolled, and uses a set of random obstacles. This went smoothly, so I’ve got a simple plugin which now just patrols points. With a little more work, it’ll be ready to pass in an arbitrary number of patrol points. The next issue is that the obstacles in OpenSteer are all spherical in nature; but it looks like the SVN version of OpenSteer includes planar, rectangular, and cubical obstacles, so I’m going to check out more recent snapshots or just go straight to SVN. With that, we should be able to dump information from SSPS directly into OpenSteer to handle steering.

Categories: Agent Core, Agent Movement, Code testing Tags:

Overhead: Threads vs. Generators vs. Function calls

September 4th, 2008 heckel No comments

Based on the profiling I ran yesterday afternoon, I see no reason not to use generator functions, and see many reasons to avoid threads. I used two different implementations of a threaded version of fibonacci sequences, a generator function version, and a standard function call. Here’s the results for the more correct version of threading (using locks and condition variables) and the others:

Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   101004    2.450    0.000    2.450    0.000 {built-in method acquire}
        1    1.299    1.299    9.577    9.577 generatortest.py:1()
   101001    1.121    0.000    4.397    0.000 threading.py:94(acquire)
   101001    1.100    0.000    2.156    0.000 threading.py:114(release)
   253005    1.071    0.000    1.477    0.000 threading.py:733(currentThread)
   253005    0.538    0.000    0.538    0.000 threading.py:44(_note)
    51001    0.479    0.000    1.267    0.000 threading.py:249(notify)
   122251    0.428    0.000    0.428    0.000 {built-in method release}
   253006    0.406    0.000    0.406    0.000 {thread.get_ident}
    51001    0.188    0.000    0.474    0.000 threading.py:149(_is_owned)
    50000    0.123    0.000    0.123    0.000 generatortest.py:20(fib)
    51000    0.107    0.000    0.107    0.000 generatortest.py:60(gimmeNum)
    50000    0.104    0.000    0.104    0.000 generatortest.py:8(fib)
    50000    0.103    0.000    0.103    0.000 generatortest.py:64(gotNum)

So using locks winds up being pretty gnarly. I thought that the condition variables should be an improvement over a while loop (either with a sleep or without a sleep), since it is possible to use notify to wake up a sleeping thread. That didn’t work. All of the methods listed there, except for the two fib functions, are part of the threaded implementation. In this case, fib (line 20) came out as more expensive than fib (line 8), though fib:8 is the generator function. This doesn’t really mean anything, as the first test (threaded w/out locks) had fib:8 as more expensive than fib:20.

For comparison, here’s the results without using locks/condition variables.

Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    2.366    2.366    4.023    4.023 generatortest.py:1()
   597325    1.258    0.000    1.258    0.000 generatortest.py:64(gotNum)
    50000    0.102    0.000    0.102    0.000 generatortest.py:66(getNum)
    50000    0.097    0.000    0.097    0.000 generatortest.py:8(fib)
    50000    0.093    0.000    0.093    0.000 generatortest.py:20(fib)
    51000    0.090    0.000    0.090    0.000 generatortest.py:60(gimmeNum)

It’s a lot quicker, but most of the time here is spent (unsurprisingly) in gotNum, which is checking to see if the thread has generated the next number in the sequence. The amount of time in getNum is a bit surprising, since that is just returning the number in the sequence. gimmeNum is a request to the thread to give the next number. fib:8 is showing up as a little slower than fib:20 here. The function state is pretty small in this case, but there isn’t much difference when a few variables are added to each function.

The short of it: It looks like generator functions are actually pretty efficient. I’ll certainly be returning to this later, to make sure this is still true when we have somewhat more demanding work going on. I was somewhat surprised that so much time was spent with the locking.

Categories: Code testing, Resource management Tags:

AgentAlpha Architecture

September 3rd, 2008 heckel No comments

The first revision of the AgentAlpha architecture (temporary name, given the current state of the engine) has five major parts:

AgentCore

  • Handles administrivia (reading in XML files, setting up logging)
  • Manages data coming in from the game world (what agent has access to what data? granularity of data?)
  • Manages actions going out from the agent
  • Manages multiple agent threads (most likely, one instance of AgentAlpha per team)

GameController

  • Connects to the game server and services (such as SSPS)
  • Receives percepts from game and services
  • Sends actions to game
  • Spawned by AgentCore

AgentController

  • One per agent
  • Builds individual agent configurations
  • Prioritizes agent behaviors
  • Manages system resources
  • Spawned by AgentCore

AgentThread

  • Runs individual behavior objects
  • Number of threads dependent on system resources
  • Spawned by AgentController

BehaviorObjects

  • Implements individual primitive behaviors
  • Each object provides generator function
  • Any-time: calling generator function next() method returns progressively better answers
  • Objects instantiated by AgentController, managed by AgentThread

Graphical representation of the architecture at the thread level (objects not represented here):

AgentAlpha Architecture

AgentAlpha Architecture

Categories: Agent Core Tags: