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)
Pointer to SPURS instance (must be 128-byte aligned)
Number of SPUs to use (1-6)
SPU thread group priority (0-255)
PPU handler thread priority (0-3071)
If true, SPU threads exit when no work is available
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)
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)
Pointer to SPURS instance
attr
const SpursAttribute*
required
Pointer to configured attributes
spursFinalize
Shutdown and cleanup a SPURS instance.
s32 spursFinalize(Spurs* spurs)
Pointer to SPURS instance
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)
Name string (max 15 characters)
spursAttributeSetSpuThreadGroupType
Configure SPU thread group type.
s32 spursAttributeSetSpuThreadGroupType(SpursAttribute* attr, int type)
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.
Set workload priorities for scheduling.
s32 spursSetPriorities(Spurs* spurs, unsigned workloadId, const u8 priorities[SPURS_MAX_SPU])
Array of priorities (one per SPU, 0-15)
Set maximum contention level for a workload.
s32 spursSetMaxContention(Spurs* spurs, unsigned workloadId, unsigned int maxContention)
Maximum number of SPUs that can run this workload simultaneously
spursGetNumSpuThread
Get the number of SPU threads in use.
s32 spursGetNumSpuThread(Spurs* spurs, unsigned *nThreads)
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
Pointer to thread count (in/out)
spursGetSpuThreadGroupId
Get the underlying SPU thread group.
s32 spursGetSpuThreadGroupId(Spurs* spurs, sys_spu_group_t* group)
Pointer to returned group ID
spursGetInfo
Get detailed information about SPURS instance.
s32 spursGetInfo(Spurs* spurs, SpursInfo *info)
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");
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
- Task granularity: Balance task size - too small = overhead, too large = poor balancing
- Number of SPUs: Use 6 or fewer for user applications
- Exit policy: Set
exitIfNoWork=true to save power during idle periods
- Priorities: Use priority levels to ensure critical tasks run first
- Contention: Limit max contention for workloads that don’t scale well
- 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