mirror of
https://github.com/izzy2lost/PSX2.git
synced 2026-07-05 15:18:36 -07:00
Merge pull request #3 from jocelino/devin/1753194409-performance-optimization
Optimize instruction cache allocation to reduce malloc/free overhead
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# PCSX2 ARM64 Performance Optimization Report
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This report identifies 5 key areas for performance optimization in the PCSX2 ARM64 emulator codebase. The analysis focused on memory allocation patterns, redundant operations, and ARM64-specific optimization opportunities.
|
||||
|
||||
## 1. Instruction Cache Reallocation Issue (HIGH PRIORITY)
|
||||
|
||||
**Location**: `app/src/main/cpp/pcsx2/x86/ix86-32/iR5900.cpp` lines 2708-2714
|
||||
|
||||
**Issue**: The instruction cache (`s_pInstCache`) is frequently reallocated during block recompilation using a naive growth strategy. Every time a block requires more instructions than the current cache size, the entire cache is freed and reallocated with only a small increment (+10 instructions).
|
||||
|
||||
**Code Pattern**:
|
||||
```cpp
|
||||
if (s_nInstCacheSize < (s_nEndBlock - startpc) / 4 + 1)
|
||||
{
|
||||
free(s_pInstCache);
|
||||
s_nInstCacheSize = (s_nEndBlock - startpc) / 4 + 10;
|
||||
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: HIGH - This occurs during every block recompilation that exceeds the current cache size, causing:
|
||||
- Frequent malloc/free operations (expensive system calls)
|
||||
- Memory fragmentation
|
||||
- Loss of existing cache data
|
||||
- Poor cache locality
|
||||
|
||||
**Solution**: Implement exponential growth strategy with data preservation to minimize future reallocations.
|
||||
|
||||
## 2. MicroVU Memory Allocation Patterns (MEDIUM PRIORITY)
|
||||
|
||||
**Location**: `app/src/main/cpp/pcsx2/x86/microVU.cpp` lines 138-139, `microVU.h` lines 190, 163, 170
|
||||
|
||||
**Issue**: Frequent `_aligned_malloc` and `_aligned_free` operations for microProgram structures and microBlockLink objects.
|
||||
|
||||
**Code Patterns**:
|
||||
```cpp
|
||||
microProgram* prog = (microProgram*)_aligned_malloc(sizeof(microProgram), 64);
|
||||
microBlockLink* newBlock = (microBlockLink*)_aligned_malloc(sizeof(microBlockLink), 32);
|
||||
_aligned_free(freeI);
|
||||
```
|
||||
|
||||
**Impact**: MEDIUM - Occurs during VU program creation/deletion:
|
||||
- Aligned memory allocation is more expensive than regular malloc
|
||||
- Frequent allocation/deallocation during emulation
|
||||
- Memory fragmentation from different alignment requirements
|
||||
|
||||
**Solution**: Implement object pools or pre-allocated memory regions for these frequently used structures.
|
||||
|
||||
## 3. Redundant Memory Clearing Operations (LOW-MEDIUM PRIORITY)
|
||||
|
||||
**Location**: Multiple files with `std::memset` patterns
|
||||
|
||||
**Issue**: Unnecessary zero-initialization of large structures, particularly:
|
||||
- `microVU_Branch.inl` lines 17-18: Clearing lpState structures
|
||||
- `iCore.cpp` lines 32, 930-933: Clearing register arrays
|
||||
- `microVU_Compile.inl`: Multiple memset operations
|
||||
|
||||
**Code Patterns**:
|
||||
```cpp
|
||||
std::memset(µVU0.prog.lpState, 0, sizeof(microVU1.prog.lpState));
|
||||
std::memset(xmmregs, 0, sizeof(xmmregs));
|
||||
std::memset(pinst, 0, sizeof(EEINST));
|
||||
```
|
||||
|
||||
**Impact**: LOW-MEDIUM - Cumulative effect across many operations:
|
||||
- Unnecessary CPU cycles spent zeroing memory
|
||||
- Some structures are immediately overwritten after clearing
|
||||
- Cache pollution from touching large memory regions
|
||||
|
||||
**Solution**: Optimize initialization patterns and avoid redundant clears where data is immediately overwritten.
|
||||
|
||||
## 4. ARM64 NEON SIMD Optimization Opportunities (MEDIUM PRIORITY)
|
||||
|
||||
**Location**: `app/src/main/cpp/pcsx2/arm64/Vif_UnpackNEON.cpp`
|
||||
|
||||
**Issue**: While the code already uses NEON instructions, there are opportunities for additional optimizations:
|
||||
|
||||
**Current Implementation Analysis**:
|
||||
- VIF unpacking uses individual NEON operations
|
||||
- Some operations could be combined or vectorized further
|
||||
- Potential for better instruction scheduling
|
||||
|
||||
**Code Example** (lines 294-295):
|
||||
```cpp
|
||||
armAsm->Shl(destReg.V4S(), destReg.V4S(), 24);
|
||||
armAsm->Ushr(destReg.V4S(), destReg.V4S(), 24);
|
||||
```
|
||||
|
||||
**Impact**: MEDIUM - Affects graphics data processing performance:
|
||||
- VIF unpacking is on the critical path for graphics rendering
|
||||
- Better NEON utilization could improve frame rates
|
||||
- ARM64-specific optimizations not fully exploited
|
||||
|
||||
**Solution**: Implement more efficient NEON instruction sequences and better utilize ARM64 capabilities.
|
||||
|
||||
## 5. Loop Optimization Opportunities (LOW-MEDIUM PRIORITY)
|
||||
|
||||
**Location**: Various files with for/while loops
|
||||
|
||||
**Issue**: Some loops could benefit from unrolling or vectorization, particularly in:
|
||||
- Memory copying operations
|
||||
- Register clearing loops
|
||||
- Block iteration patterns
|
||||
|
||||
**Examples**:
|
||||
- `BaseblockEx.cpp` lines 76-80: Simple iteration that could be unrolled
|
||||
- `microVU.h` lines 127-129, 253-255: Loops over fixed-size arrays
|
||||
|
||||
**Impact**: LOW-MEDIUM - Depends on loop frequency:
|
||||
- Hot loops in recompilation paths could benefit from optimization
|
||||
- Some loops are over small, fixed-size arrays suitable for unrolling
|
||||
- Profile-guided optimization needed to identify highest impact loops
|
||||
|
||||
**Solution**: Profile-guided optimization of hot loops with unrolling or vectorization where appropriate.
|
||||
|
||||
## Performance Impact Assessment
|
||||
|
||||
| Issue | Priority | Frequency | Impact per Operation | Overall Impact |
|
||||
|-------|----------|-----------|---------------------|----------------|
|
||||
| Instruction Cache Reallocation | HIGH | Every oversized block | High | HIGH |
|
||||
| MicroVU Memory Allocation | MEDIUM | VU program lifecycle | Medium | MEDIUM |
|
||||
| Redundant Memory Clearing | LOW-MEDIUM | Various operations | Low | LOW-MEDIUM |
|
||||
| NEON Optimizations | MEDIUM | Graphics processing | Medium | MEDIUM |
|
||||
| Loop Optimizations | LOW-MEDIUM | Various | Low-Medium | LOW-MEDIUM |
|
||||
|
||||
## Recommendation
|
||||
|
||||
**Immediate Action**: Implement the instruction cache optimization (Issue #1) as it has the highest impact and is straightforward to fix.
|
||||
|
||||
**Future Work**: Address the MicroVU memory allocation patterns and explore additional NEON optimizations for graphics performance improvements.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The instruction cache optimization should:
|
||||
1. Use exponential growth (doubling) to reduce future reallocations
|
||||
2. Preserve existing cache data during resize operations
|
||||
3. Maintain the same API and behavior
|
||||
4. Follow existing error handling patterns in the codebase
|
||||
|
||||
This optimization will significantly reduce malloc/free overhead during block recompilation, which is a critical performance path in the emulator.
|
||||
@@ -2707,10 +2707,21 @@ StartRecomp:
|
||||
{
|
||||
if (s_nInstCacheSize < (s_nEndBlock - startpc) / 4 + 1)
|
||||
{
|
||||
const u32 required_size = (s_nEndBlock - startpc) / 4 + 10;
|
||||
const u32 new_size = std::max(required_size, s_nInstCacheSize * 2);
|
||||
|
||||
EEINST* new_cache = (EEINST*)malloc(sizeof(EEINST) * new_size);
|
||||
if (!new_cache)
|
||||
pxFailRel("Failed to allocate R5900 InstCache array");
|
||||
|
||||
if (s_pInstCache && s_nInstCacheSize > 0)
|
||||
{
|
||||
memcpy(new_cache, s_pInstCache, sizeof(EEINST) * s_nInstCacheSize);
|
||||
}
|
||||
|
||||
free(s_pInstCache);
|
||||
s_nInstCacheSize = (s_nEndBlock - startpc) / 4 + 10;
|
||||
s_pInstCache = (EEINST*)malloc(sizeof(EEINST) * s_nInstCacheSize);
|
||||
pxAssert(s_pInstCache != NULL);
|
||||
s_pInstCache = new_cache;
|
||||
s_nInstCacheSize = new_size;
|
||||
}
|
||||
|
||||
EEINST* pcur = s_pInstCache + (s_nEndBlock - startpc) / 4;
|
||||
|
||||
Reference in New Issue
Block a user