eurostat / gridviz

A package for visualizing gridded data 🌐
https://github.com/eurostat/gridviz
European Union Public License 1.2
194 stars 10 forks source link

To reduce time "waiting on main thread," #131

Open jgaffuri opened 1 month ago

jgaffuri commented 1 month ago

Consider: Minimizing heavy JavaScript computations by using Web Workers or async/await. Deferring or lazily loading non-essential resources to reduce initial load. Breaking up large tasks into smaller, asynchronous tasks using requestAnimationFrame or setTimeout. Optimizing DOM manipulation by reducing unnecessary changes and batch updates.

joewdavies commented 4 days ago

Some advice from Mr. Chat:

Suggestions for Improving Grid Rendering Efficiency

Here are several strategies to optimize the rendering logic for your gridviz library:


1. Optimize Loop Logic and Data Processing

Precompute Values

Filter and Sort Early

Combine Similar Properties


2. Use Efficient Drawing Techniques

Batch Drawing Operations

Instead of setting styles (fillStyle, etc.) or transformations repeatedly within the loop, group cells by shared attributes like color, shape, and size and render them in batches.


3. Leverage Web Workers

Offload heavy computations (e.g., filtering, sorting, or calculating offsets) to a Web Worker. For example:

  1. Send raw cells data to the worker.
  2. Perform filtering, scaling, and shape assignment in the worker.
  3. Return a lightweight set of instructions (e.g., [{x, y, size, color, shape}, ...]) to the main thread for rendering.

This frees up the main thread for UI updates and rendering, reducing perceived lag.


4. Use RequestAnimationFrame

Wrap rendering logic in requestAnimationFrame to ensure it aligns with the browser's refresh rate, minimizing dropped frames and enhancing visual smoothness:

function renderFrame() {
    requestAnimationFrame(() => {
        gridviz.redraw(); // Call your redraw logic
    });
}

5. Reduce Canvas State Changes

Minimize state changes like ctx.globalAlpha, ctx.globalCompositeOperation, and transformations (setCanvasTransform). Group cells with similar properties to reduce the number of changes:


6. Optimize Geometric Calculations

Instead of recalculating positions and offsets for every cell:


7. Reduce Layer Overhead


8. Profile and Optimize Hotspots

Use Chrome DevTools or similar tools to identify rendering bottlenecks:

  1. Record a performance profile while zooming and panning.
  2. Identify functions consuming the most time.
  3. Optimize based on findings (e.g., caching results of computationally expensive calls).

9. Consider WebGL for Larger Datasets

If datasets are massive and 2D Canvas becomes a bottleneck:


10. Incremental Rendering

Divide the rendering into chunks to avoid blocking the main thread:

  1. Render visible cells first.
  2. Load and render additional cells progressively in subsequent animation frames.

Implementation Example: Web Worker + Canvas Rendering

Here’s a basic outline for combining Web Workers and canvas rendering:

Main Thread

const worker = new Worker('dataProcessor.js');
worker.onmessage = (event) => {
    const processedData = event.data; // Lightweight rendering instructions
    renderCells(processedData);      // Render on canvas
};
worker.postMessage({ cells, resolution, zoomLevel });
Web Worker (dataProcessor.js)
onmessage = (event) => {
    const { cells, resolution, zoomLevel } = event.data;
    // Perform filtering and offset calculations
    const processed = cells.map(c => ({
        x: c.x,
        y: c.y,
        color: computeColor(c, zoomLevel),
        size: computeSize(c, resolution, zoomLevel),
        shape: computeShape(c, zoomLevel),
    })).filter(c => c.color !== 'none');
    postMessage(processed);
};

This approach distributes work across threads and accelerates rendering on the main thread.

joewdavies commented 4 days ago

Important: workers can't directly accept functions as arguments. This means that dataset.preprocess cannot be passed to a web worker.

joewdavies commented 4 days ago

Some devTools performance results using TiledGrid with ShapeColorSizeStyle:

image

image

joewdavies commented 3 days ago

Some useful guidance: https://web.dev/articles/optimize-long-tasks?utm_source=devtools