# How to Analyze Mesh Optimization Effectiveness with meshopt_analyzeVertexCache

> Analyze mesh optimization effectiveness with meshopt_analyzeVertexCache. Learn ACMR and ATVR metrics to quantify GPU transform cache efficiency. Optimize your index buffers today.

- Repository: [Arseny Kapoulkine/meshoptimizer](https://github.com/zeux/meshoptimizer)
- Tags: how-to-guide
- Published: 2026-07-11

---

**The `meshopt_analyzeVertexCache` function simulates a FIFO vertex cache to calculate ACMR (Average Cache Miss Ratio) and ATVR (Average Transformed Vertex Ratio), providing hardware-agnostic metrics that quantify how efficiently an index buffer utilizes GPU transform caches.**

The `meshopt_analyzeVertexCache` analyzer is part of the meshoptimizer library’s efficiency analyzer suite, designed to evaluate vertex cache utilization without requiring actual GPU profiling. By modeling a simplified FIFO cache, it helps developers identify inefficient index ordering and validate the impact of optimizations like `meshopt_optimizeVertexCache` before deployment.

## Understanding Vertex Cache Statistics

The analyzer returns a `meshopt_VertexCacheStatistics` structure defined in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) containing four key metrics:

- **`vertices_transformed`** – Total vertex shader invocations simulated while processing the index buffer.
- **`warps_executed`** – Number of warp executions, relevant for GPUs that process vertices in groups.
- **`acmr`** – *Average Cache Miss Ratio* (`vertices_transformed / triangle_count`). The theoretical best-case is approximately 0.5 (each vertex reused between triangles), while the worst-case is 3.0 (unique vertices per triangle).
- **`atvr`** – *Average Transformed Vertex Ratio* (`vertices_transformed / vertex_count`). The optimal value is 1.0, indicating each unique vertex is transformed exactly once.

According to the meshoptimizer documentation, well-optimized real-world meshes typically achieve ACMR values between 0.5 and 1.5, with ATVR values close to 1.0.

## How meshopt_analyzeVertexCache Works

The function simulates cache behavior by walking the index buffer and maintaining a FIFO cache of configurable size. As implemented in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) (lines 36-50), the analyzer accepts the following parameters:

1. **`indices`** – Pointer to the index buffer array.
2. **`index_count`** – Total number of indices in the buffer.
3. **`vertex_count`** – Number of unique vertices in the mesh.
4. **`cache_size`** – Size of the simulated FIFO cache (default 16 entries, common on many GPUs).
5. **`warp_size`** – Number of vertices processed per warp (default 0, indicating no warp grouping).
6. **`primgroup_size`** – Size of primitive groups for parallel processing (default 0).

During simulation, when a vertex index is not present in the cache, a cache miss occurs and the vertex is marked as transformed. The function accumulates these statistics and computes the final ratios. Because this model is hardware-agnostic, the results approximate actual GPU performance rather than matching specific hardware counters exactly.

## Implementing Cache Analysis

### Basic Usage

To analyze a mesh’s cache efficiency, provide the index buffer and vertex count along with your target cache configuration:

```cpp
#include "meshoptimizer.h"
#include <vector>
#include <iostream>

int main()
{
    // Example mesh data
    std::vector<unsigned int> indices = {0, 1, 2, 2, 1, 3}; // Two triangles
    size_t vertexCount = 4;

    // Analyze with a 16-entry cache (common default)
    meshopt_VertexCacheStatistics stats =
        meshopt_analyzeVertexCache(
            indices.data(),
            indices.size(),
            vertexCount,
            16,   // cache size
            0,    // warp size (0 = no warp grouping)
            0);   // primitive group size

    std::cout << "ACMR: " << stats.acmr << "\n";
    std::cout << "ATVR: " << stats.atvr << "\n";
    std::cout << "Vertex transforms: " << stats.vertices_transformed << "\n";
}

```

### Comparing Before and After Optimization

The analyzer is most valuable when measuring the impact of index reordering. The repository’s [`demo/main.cpp`](https://github.com/zeux/meshoptimizer/blob/main/demo/main.cpp) (lines 501-506) demonstrates this pattern:

```cpp
// Analyze original order
meshopt_VertexCacheStatistics before = meshopt_analyzeVertexCache(
    idx.data(), idx.size(), vertexCount, 16, 0, 0);

// Optimize vertex cache locality
std::vector<unsigned int> optimized(idx.size());
meshopt_optimizeVertexCache(
    optimized.data(), 
    idx.data(), 
    idx.size(), 
    vertexCount);

// Analyze optimized order
meshopt_VertexCacheStatistics after = meshopt_analyzeVertexCache(
    optimized.data(), optimized.size(), vertexCount, 16, 0, 0);

printf("ACMR improved from %.3f to %.3f\n", before.acmr, after.acmr);
printf("ATVR improved from %.3f to %.3f\n", before.atvr, after.atvr);

```

### Custom Cache Configurations

For hardware-specific tuning, adjust the cache parameters to match your target GPU architecture. The [`tools/vcachetuner.cpp`](https://github.com/zeux/meshoptimizer/blob/main/tools/vcachetuner.cpp) utility demonstrates profiling across various cache profiles:

```cpp
// Simulate NVIDIA-style architecture with 32-entry cache and 32-vertex warps
meshopt_VertexCacheStatistics nvStats = meshopt_analyzeVertexCache(
    idx.data(), 
    idx.size(), 
    vertexCount, 
    32,  // cache size
    32,  // warp size
    32); // primitive group size

```

## Interpreting the Results

Use these ranges to evaluate your meshopt_analyzeVertexCache output:

- **ACMR ≤ 0.5** – Near-optimal. Vertices are heavily reused between adjacent triangles.
- **ACMR 0.5–1.5** – Typical for well-optimized meshes. This range indicates good cache locality.
- **ACMR > 1.5** – Poor cache efficiency. Significant vertex shader overdraw suggests the index buffer needs reordering.

An **ATVR significantly above 1.0** indicates vertex splits—duplicate vertices that could be shared but are processed multiple times due to suboptimal indexing. If you observe high ATVR, running `meshopt_optimizeVertexCache` followed by `meshopt_optimizeVertexFetch` typically reduces both metrics.

## Summary

- **`meshopt_analyzeVertexCache`** simulates a FIFO vertex cache to compute ACMR and ATVR metrics without GPU hardware access.
- The function is declared in [`src/meshoptimizer.h`](https://github.com/zeux/meshoptimizer/blob/main/src/meshoptimizer.h) and returns `vertices_transformed`, `warps_executed`, `acmr`, and `atvr`.
- **ACMR** targets 0.5–1.5 for optimized meshes, while **ATVR** targets 1.0.
- Compare statistics before and after `meshopt_optimizeVertexCache` to quantify optimization gains.
- Customize `cache_size`, `warp_size`, and `primgroup_size` to model specific GPU architectures as shown in [`tools/vcachetuner.cpp`](https://github.com/zeux/meshoptimizer/blob/main/tools/vcachetuner.cpp).

## Frequently Asked Questions

### What is the difference between ACMR and ATVR?

**ACMR (Average Cache Miss Ratio)** measures vertex shader invocations per triangle, indicating how well adjacent triangles share vertex data in the cache. **ATVR (Average Transformed Vertex Ratio)** measures total vertex transforms relative to unique vertex count, revealing duplicate vertex processing. ACMR focuses on cache efficiency, while ATVR identifies vertex duplication issues.

### What cache size should I use for meshopt_analyzeVertexCache?

A **16-entry cache** (the default) approximates many mobile and desktop GPUs, making it a safe general-purpose choice. For specific hardware targeting, use **32 entries** for modern NVIDIA GPUs or consult your target hardware’s specifications. The [`tools/vcachetuner.cpp`](https://github.com/zeux/meshoptimizer/blob/main/tools/vcachetuner.cpp) file in the repository demonstrates profiling across multiple cache sizes.

### Can meshopt_analyzeVertexCache predict exact GPU performance?

No, the analyzer provides **hardware-agnostic approximations** using a simplified FIFO model. While ACMR and ATVR correlate strongly with actual GPU performance, they cannot account for complex hardware behaviors like cache associativity, prefetching, or warp scheduling. Use these metrics for relative comparisons between optimization algorithms rather than absolute performance guarantees.

### Why is my ATVR higher than 1.0 after optimization?

An ATVR above 1.0 indicates that vertices are being transformed multiple times, typically due to **vertex splits** where geometric duplicates exist in the vertex buffer. While `meshopt_optimizeVertexCache` improves index ordering, it cannot merge duplicate vertices. Run `meshopt_optimizeVertexFetch` after cache optimization to remap indices and reduce vertex duplication, which should lower ATVR toward 1.0.