Skip to main content

Overview

SPURS (SPU Runtime System) is a sophisticated task scheduling framework for managing SPU workloads on the PlayStation 3. It provides dynamic load balancing, priority scheduling, and workload management across multiple SPUs.

Key Features

  • Dynamic scheduling: Automatically distributes tasks across available SPUs
  • Priority levels: 16 priority levels per workload
  • Load balancing: Work-stealing algorithm for efficient utilization
  • Event-driven: Integration with event queues for coordination
  • Preemption support: Higher priority tasks can preempt lower priority ones
  • Trace support: Built-in profiling and debugging capabilities

When to Use SPURS

  • Variable workload with many small tasks
  • Dynamic task dependencies
  • Need automatic load balancing
  • Multiple independent workloads
For fixed pipelines with 6 or fewer threads, raw SPU threads may be simpler.

Initialization

spursInitialize

Initialize a SPURS instance.
s32 spursInitialize(Spurs* spurs, unsigned nSpus, int spuPriority, int ppuPriority, bool exitIfNoWork)
spurs
Spurs*
required
Pointer to SPURS instance (must be 128-byte aligned)
nSpus
unsigned
required
Number of SPUs to use (1-6)
spuPriority
int
required
SPU thread group priority (0-255)
ppuPriority
int
required
PPU handler thread priority (0-3071)
exitIfNoWork
bool
required
If true, SPU threads exit when no work is available
return
s32
Zero if successful, nonzero on error

spursAttributeInitialize

Initialize SPURS attributes for advanced configuration.
s32 spursAttributeInitialize(SpursAttribute *attr, u8 nSpus, s32 spuPriority, s32 ppuPriority, bool exitIfNoWork)
attr
SpursAttribute*
required
Pointer to attribute structure (8-byte aligned)
Similar parameters to spursInitialize.

spursInitializeWithAttribute

Initialize SPURS using a configured attribute structure.
s32 spursInitializeWithAttribute(Spurs* spurs, const SpursAttribute* attr)
spurs
Spurs*
required
Pointer to SPURS instance
attr
const SpursAttribute*
required
Pointer to configured attributes

spursFinalize

Shutdown and cleanup a SPURS instance.
s32 spursFinalize(Spurs* spurs)
spurs
Spurs*
required
Pointer to SPURS instance
return
s32
Zero if successful, nonzero on error

Attribute Configuration

spursAttributeSetNamePrefix

Set a name prefix for debugging.
s32 spursAttributeSetNamePrefix(SpursAttribute* attr, const char* name, size_t size)
attr
SpursAttribute*
required
Attribute structure
name
const char*
required
Name string (max 15 characters)
size
size_t
required
Length of name string

spursAttributeSetSpuThreadGroupType

Configure SPU thread group type.
s32 spursAttributeSetSpuThreadGroupType(SpursAttribute* attr, int type)
type
int
required
Thread group type (use SPU_THREAD_GROUP_TYPE_NORMAL or other constants)

spursAttributeEnableSpuPrintfIfAvailable

Enable SPU printf support for debugging.
s32 spursAttributeEnableSpuPrintfIfAvailable(SpursAttribute* attr)

spursAttributeSetMemoryContainerForSpuThread

Assign a memory container for SPU threads.
s32 spursAttributeSetMemoryContainerForSpuThread(SpursAttribute* attr, sys_mem_container_t container)
container
sys_mem_container_t
required
Memory container ID

Runtime Control

spursWakeUp

Wake up SPURS SPU threads if they’re sleeping.
s32 spursWakeUp(Spurs* spurs)
Useful when exitIfNoWork is false and SPUs are waiting for tasks.

spursSetPriorities

Set workload priorities for scheduling.
s32 spursSetPriorities(Spurs* spurs, unsigned workloadId, const u8 priorities[SPURS_MAX_SPU])
spurs
Spurs*
required
SPURS instance
workloadId
unsigned
required
Workload identifier
priorities
const u8[]
required
Array of priorities (one per SPU, 0-15)

spursSetMaxContention

Set maximum contention level for a workload.
s32 spursSetMaxContention(Spurs* spurs, unsigned workloadId, unsigned int maxContention)
maxContention
unsigned int
required
Maximum number of SPUs that can run this workload simultaneously

Information Queries

spursGetNumSpuThread

Get the number of SPU threads in use.
s32 spursGetNumSpuThread(Spurs* spurs, unsigned *nThreads)
nThreads
unsigned*
required
Pointer to returned thread count

spursGetSpuThreadId

Get SPU thread IDs.
s32 spursGetSpuThreadId(Spurs* spurs, sys_spu_thread_t* threads, unsigned *nThreads)
threads
sys_spu_thread_t*
required
Array to receive thread IDs
nThreads
unsigned*
required
Pointer to thread count (in/out)

spursGetSpuThreadGroupId

Get the underlying SPU thread group.
s32 spursGetSpuThreadGroupId(Spurs* spurs, sys_spu_group_t* group)
group
sys_spu_group_t*
required
Pointer to returned group ID

spursGetInfo

Get detailed information about SPURS instance.
s32 spursGetInfo(Spurs* spurs, SpursInfo *info)
info
SpursInfo*
required
Pointer to info structure to fill
The SpursInfo structure contains:
typedef struct SpursInfo {
    int nSpus;
    int spuGroupPriority;
    int ppuThreadPriority;
    bool exitIfNoWork;
    bool spurs2;  // Extended SPURS version
    void *traceBuffer;
    u64 traceBufferSize;
    u32 traceMode;
    sys_spu_group_t spuGroup;
    sys_spu_thread_t spuThreads[SPURS_MAX_SPU];
    sys_ppu_thread_t spursHandlerThread0;
    sys_ppu_thread_t spursHandlerThread1;
    char namePrefix[SPURS_NAME_MAX_LENGTH+1];
    size_t namePrefixLength;
    u32 deadlineMissCounter;
    u32 deadlineMeetCounter;
} SpursInfo;

spursGetSpuGuid

Get GUID for SPU program identification.
s32 spursGetSpuGuid(const void* pSpuGUID, uint64_t *dest)

Example Usage

Basic SPURS Initialization

#include <spurs/spurs.h>

// Allocate SPURS instance (must be 128-byte aligned)
Spurs spurs __attribute__((aligned(128)));

// Initialize with 4 SPUs
if (spursInitialize(&spurs, 4, 100, 1000, false) != 0) {
    printf("SPURS initialization failed\n");
    return -1;
}

printf("SPURS initialized successfully\n");

// ... create tasks and workloads ...

// Cleanup
spursFinalize(&spurs);

Advanced Initialization with Attributes

Spurs spurs __attribute__((aligned(128)));
SpursAttribute attr __attribute__((aligned(8)));

// Initialize attributes
spursAttributeInitialize(&attr, 6, 100, 1000, false);

// Configure attributes
spursAttributeSetNamePrefix(&attr, "GameEngine", 10);
spursAttributeEnableSpuPrintfIfAvailable(&attr);
spursAttributeSetSpuThreadGroupType(&attr, SPU_THREAD_GROUP_TYPE_NORMAL);

// Initialize SPURS
if (spursInitializeWithAttribute(&spurs, &attr) != 0) {
    printf("SPURS initialization failed\n");
    return -1;
}

printf("SPURS initialized with custom attributes\n");

Querying SPURS Information

Spurs spurs __attribute__((aligned(128)));
spursInitialize(&spurs, 6, 100, 1000, false);

// Get basic info
unsigned nThreads;
spursGetNumSpuThread(&spurs, &nThreads);
printf("Number of SPU threads: %u\n", nThreads);

// Get detailed info
SpursInfo info;
spursGetInfo(&spurs, &info);

printf("SPURS Configuration:\n");
printf("  Name: %s\n", info.namePrefix);
printf("  SPUs: %d\n", info.nSpus);
printf("  SPU Priority: %d\n", info.spuGroupPriority);
printf("  PPU Priority: %d\n", info.ppuThreadPriority);
printf("  Exit if no work: %s\n", info.exitIfNoWork ? "yes" : "no");
printf("  Deadline misses: %u\n", info.deadlineMissCounter);
printf("  Deadlines met: %u\n", info.deadlineMeetCounter);

// Get thread IDs
sys_spu_thread_t threads[SPURS_MAX_SPU];
unsigned count = SPURS_MAX_SPU;
spursGetSpuThreadId(&spurs, threads, &count);

for (unsigned i = 0; i < count; i++) {
    printf("  SPU Thread %u: ID 0x%x\n", i, threads[i]);
}

Dynamic Priority Adjustment

Spurs spurs __attribute__((aligned(128)));
spursInitialize(&spurs, 6, 100, 1000, false);

unsigned workloadId = 0;  // Assume we have a workload

// Set different priorities for each SPU
u8 priorities[SPURS_MAX_SPU] = {
    15,  // SPU 0: Highest priority
    15,  // SPU 1: Highest priority
    10,  // SPU 2: Medium priority
    10,  // SPU 3: Medium priority
    5,   // SPU 4: Low priority
    5    // SPU 5: Low priority
};

spursSetPriorities(&spurs, workloadId, priorities);

// Limit workload to 4 SPUs maximum
spursSetMaxContention(&spurs, workloadId, 4);

printf("Workload priorities and contention configured\n");

SPURS vs Raw SPU Threads

// SPURS Approach (dynamic tasks)
Spurs spurs __attribute__((aligned(128)));
spursInitialize(&spurs, 6, 100, 1000, false);

// Submit many small tasks
for (int i = 0; i < 1000; i++) {
    // SPURS will automatically schedule across SPUs
    // submitTask(&spurs, task[i]);
}

// Raw SPU Thread Approach (fixed pipeline)
sysSpuImage image;
sysSpuImageOpen(&image, "/app_home/worker.elf");

sys_spu_group_t group;
sysSpuThreadGroupCreate(&group, 6, 100, &groupAttr);

for (int i = 0; i < 6; i++) {
    sys_spu_thread_t thread;
    sysSpuThreadInitialize(&thread, group, i, &image, &threadAttr, &args);
}

sysSpuThreadGroupStart(group);
// Each SPU runs fixed program

Constants

#define SPURS_MAX_SPU 8  // Maximum SPUs (6 usable)
#define SPURS_NAME_MAX_LENGTH 15
#define SPURS_ALIGN 128
#define SPURS_SIZE 4096   // Size of Spurs structure
#define SPURS_SIZE2 8192  // Size of Spurs2 structure
#define SPURS_ATTRIBUTE_ALIGN 8
#define SPURS_ATTRIBUTE_SIZE 512

Performance Tips

  1. Task granularity: Balance task size - too small = overhead, too large = poor balancing
  2. Number of SPUs: Use 6 or fewer for user applications
  3. Exit policy: Set exitIfNoWork=true to save power during idle periods
  4. Priorities: Use priority levels to ensure critical tasks run first
  5. Contention: Limit max contention for workloads that don’t scale well
  6. Memory: SPURS structure is 4KB, allocate on 128-byte boundary

Integration with Other Systems

SPURS integrates with:
  • Event Queues: Task completion notifications
  • Job Queue: High-level task submission
  • SPU Threads: Can mix SPURS and raw SPU threads
  • Trace System: Performance profiling and debugging

See Also

Build docs developers (and LLMs) love