PlatformFactors.java

package com.morphiqlabs.wavelet.performance;

import java.io.Serializable;

/**
 * Platform-specific factors that affect performance.
 * 
 * <p>These factors are used to adjust the base performance model to match
 * the characteristics of the specific hardware platform.</p>
 */
public class PlatformFactors implements Serializable {
    private static final long serialVersionUID = 1L;
    
    /**
     * Volatile field to prevent dead code elimination in benchmarks.
     * This is a standard JMH-style blackhole pattern to ensure the compiler
     * doesn't optimize away our benchmark computations.
     */
    private static volatile double blackhole;
    
    /**
     * Number of double elements in AVX-512 vector (512 bits / 64 bits per double).
     */
    private static final int AVX512_DOUBLES_PER_VECTOR = 8;
    
    /**
     * Estimated speedup factors for different vector instruction sets.
     */
    private static final double NEON_SPEEDUP = 2.0;
    private static final double AVX2_SPEEDUP = 4.0;
    private static final double AVX512_SPEEDUP = 8.0;
    
    /**
     * Reference benchmark time in nanoseconds (~100ms).
     * This is the expected time for the benchmark on the reference platform.
     */
    private static final double REFERENCE_BENCHMARK_TIME_NS = 100_000_000;
    
    /**
     * Benchmark parameters for CPU speed estimation.
     */
    private static final int BENCHMARK_ARRAY_SIZE = 1_000_000;
    private static final int BENCHMARK_ITERATIONS = 10;
    
    /**
     * CPU speed factor relative to reference platform (1.0 = reference speed).
     */
    public final double cpuSpeedFactor;
    
    /**
     * Speedup factor when using vector instructions.
     */
    public final double vectorSpeedup;
    
    /**
     * L1 cache size in bytes.
     */
    public final long l1CacheSize;
    
    /**
     * L2 cache size in bytes.
     */
    public final long l2CacheSize;
    
    /**
     * L3 cache size in bytes.
     */
    public final long l3CacheSize;
    
    /**
     * Number of CPU cores available.
     */
    public final int coreCount;
    
    /**
     * Platform architecture (x86, ARM, etc.).
     */
    public final String architecture;
    
    /**
     * Whether the platform supports AVX-512.
     */
    public final boolean hasAVX512;
    
    /**
     * Whether the platform supports ARM NEON.
     */
    public final boolean hasNEON;
    
    /**
     * Memory bandwidth in GB/s.
     */
    public final double memoryBandwidth;
    
    private PlatformFactors(Builder builder) {
        this.cpuSpeedFactor = builder.cpuSpeedFactor;
        this.vectorSpeedup = builder.vectorSpeedup;
        this.l1CacheSize = builder.l1CacheSize;
        this.l2CacheSize = builder.l2CacheSize;
        this.l3CacheSize = builder.l3CacheSize;
        this.coreCount = builder.coreCount;
        this.architecture = builder.architecture;
        this.hasAVX512 = builder.hasAVX512;
        this.hasNEON = builder.hasNEON;
        this.memoryBandwidth = builder.memoryBandwidth;
    }
    
    /**
     * Detects platform factors for the current system.
     * 
     * @return Platform factors for this system
     */
    public static PlatformFactors detectPlatform() {
        Builder builder = new Builder();
        
        // Detect architecture
        String arch = System.getProperty("os.arch").toLowerCase();
        builder.architecture(arch);
        
        // Detect core count
        builder.coreCount(Runtime.getRuntime().availableProcessors());
        
        // Estimate CPU speed factor based on simple benchmark
        builder.cpuSpeedFactor(estimateCpuSpeedFactor());
        
        // Detect vector capabilities
        if (arch.contains("aarch64") || arch.contains("arm")) {
            builder.hasNEON(true);
            builder.vectorSpeedup(NEON_SPEEDUP);
        } else if (arch.contains("x86") || arch.contains("amd64")) {
            // Check for AVX-512 (simplified check)
            boolean hasAVX512 = checkAVX512Support();
            builder.hasAVX512(hasAVX512);
            builder.vectorSpeedup(hasAVX512 ? AVX512_SPEEDUP : AVX2_SPEEDUP);
        }
        
        // Estimate cache sizes (platform-specific defaults)
        if (arch.contains("aarch64")) {
            // Apple Silicon typical values
            builder.l1CacheSize(128 * 1024);      // 128 KB per core
            builder.l2CacheSize(4 * 1024 * 1024); // 4 MB shared
            builder.l3CacheSize(16 * 1024 * 1024); // 16 MB shared
            builder.memoryBandwidth(68.0); // GB/s for M1
        } else {
            // x86-64 typical values
            builder.l1CacheSize(32 * 1024);       // 32 KB per core
            builder.l2CacheSize(256 * 1024);      // 256 KB per core
            builder.l3CacheSize(8 * 1024 * 1024); // 8 MB shared
            builder.memoryBandwidth(50.0); // GB/s typical DDR4
        }
        
        return builder.build();
    }
    
    /**
     * Creates platform factors for a reference system.
     * 
     * @return Reference platform factors
     */
    public static PlatformFactors referencePlatform() {
        return new Builder()
            .cpuSpeedFactor(1.0)
            .vectorSpeedup(1.0)
            .l1CacheSize(32 * 1024)
            .l2CacheSize(256 * 1024)
            .l3CacheSize(8 * 1024 * 1024)
            .coreCount(4)
            .architecture("x86_64")
            .hasAVX512(false)
            .hasNEON(false)
            .memoryBandwidth(25.0)
            .build();
    }
    
    private static double estimateCpuSpeedFactor() {
        // Simple CPU speed estimation using array operations
        double[] data = new double[BENCHMARK_ARRAY_SIZE];
        
        // Warm up
        for (int i = 0; i < BENCHMARK_ARRAY_SIZE; i++) {
            data[i] = i * 0.5;
        }
        
        // Measure time for simple operations
        long start = System.nanoTime();
        double sum = 0;
        for (int iter = 0; iter < BENCHMARK_ITERATIONS; iter++) {
            for (int i = 0; i < BENCHMARK_ARRAY_SIZE; i++) {
                sum += data[i] * data[i];
            }
        }
        long elapsed = System.nanoTime() - start;
        
        // Prevent dead code elimination by storing result to volatile field
        blackhole = sum;
        
        return REFERENCE_BENCHMARK_TIME_NS / elapsed;
    }
    
    private static boolean checkAVX512Support() {
        // Simplified check - in production, use CPUID or JNI
        try {
            // Check if Vector API reports 512-bit vectors
            Class<?> vectorClass = Class.forName("jdk.incubator.vector.DoubleVector");
            Object species = vectorClass.getField("SPECIES_PREFERRED").get(null);
            int length = (int) species.getClass().getMethod("length").invoke(species);
            return length >= AVX512_DOUBLES_PER_VECTOR;
        } catch (Exception e) {
            return false;
        }
    }
    
    /**
     * Builder for platform factors.
     */
    public static class Builder {
        private double cpuSpeedFactor = 1.0;
        private double vectorSpeedup = 1.0;
        private long l1CacheSize = 32 * 1024;
        private long l2CacheSize = 256 * 1024;
        private long l3CacheSize = 8 * 1024 * 1024;
        private int coreCount = 4;
        private String architecture = "unknown";
        private boolean hasAVX512 = false;
        private boolean hasNEON = false;
        private double memoryBandwidth = 25.0;
        
        public Builder cpuSpeedFactor(double factor) {
            this.cpuSpeedFactor = factor;
            return this;
        }
        
        public Builder vectorSpeedup(double speedup) {
            this.vectorSpeedup = speedup;
            return this;
        }
        
        public Builder l1CacheSize(long size) {
            this.l1CacheSize = size;
            return this;
        }
        
        public Builder l2CacheSize(long size) {
            this.l2CacheSize = size;
            return this;
        }
        
        public Builder l3CacheSize(long size) {
            this.l3CacheSize = size;
            return this;
        }
        
        public Builder coreCount(int count) {
            this.coreCount = count;
            return this;
        }
        
        public Builder architecture(String arch) {
            this.architecture = arch;
            return this;
        }
        
        public Builder hasAVX512(boolean has) {
            this.hasAVX512 = has;
            return this;
        }
        
        public Builder hasNEON(boolean has) {
            this.hasNEON = has;
            return this;
        }
        
        public Builder memoryBandwidth(double bandwidth) {
            this.memoryBandwidth = bandwidth;
            return this;
        }
        
        public PlatformFactors build() {
            return new PlatformFactors(this);
        }
    }
    
    @Override
    public String toString() {
        return String.format(
            "PlatformFactors{arch=%s, cores=%d, cpuSpeed=%.2fx, vectorSpeedup=%.1fx, " +
            "L1=%dKB, L2=%dKB, L3=%dMB, memBW=%.1fGB/s, AVX512=%b, NEON=%b}",
            architecture, coreCount, cpuSpeedFactor, vectorSpeedup,
            l1CacheSize / 1024, l2CacheSize / 1024, l3CacheSize / (1024 * 1024),
            memoryBandwidth, hasAVX512, hasNEON
        );
    }
}