tylerlong / manate

3 stars 2 forks source link

Can we have a option that only watch the top level changes? #5

Open zxdong262 opened 1 week ago

zxdong262 commented 1 week ago

As the benchmark shows, use json string as state would beat directly use large array in read.

// test/benchmarks/b1.js

const Benchmark = require('benchmark')
const { manage } = require('manate')

// Test data size configurations
const ARRAY_SIZES = [20, 100, 1000, 10000]

// Helper function to generate test data
function generateTestData (size) {
  return Array.from({ length: size }, (_, i) => ({
    id: i,
    value: `value-${i}`,
    timestamp: Date.now()
  }))
}

// JSON string store with manate
class JsonStore {
  constructor (largeArray) {
    this.store = manage({
      _data: JSON.stringify(largeArray)
    }, 1)
  }

  getData () {
    return JSON.parse(this.store._data)
  }

  setItem (index, value) {
    const data = this.getData()
    data[index] = value
    this.store._data = JSON.stringify(data)
  }
}

// JS array store with manate
class ArrayStore {
  constructor (largeArray) {
    this.store = manage({
      data: largeArray
    })
  }

  getData () {
    return this.store.data
  }

  setItem (index, value) {
    this.store.data[index] = value
  }
}

// Create benchmark suite
const suite = new Benchmark.Suite()

// Add benchmarks for each array size
ARRAY_SIZES.forEach(size => {
  const testData = generateTestData(size)

  suite
    .add(`JSON Store (size ${size}) - Read`, function () {
      const store = new JsonStore(testData)
      global.x = store.getData()
    })
    .add(`Array Store (size ${size}) - Read`, function () {
      const store = new ArrayStore(testData)
      global.x = store.getData()
    })
    .add(`JSON Store (size ${size}) - Write`, function () {
      const store = new JsonStore(testData)
      const n = Math.floor(Math.random() * size)
      store.setItem(n, { id: 1, value: 'new-value', timestamp: Date.now() })
    })
    .add(`Array Store (size ${size}) - Write`, function () {
      const store = new ArrayStore(testData)
      const n = Math.floor(Math.random() * size)
      store.setItem(n, { id: 1, value: 'new-value', timestamp: Date.now() })
    })
})

// Add listeners
suite
  .on('cycle', function (event) {
    console.log(String(event.target))
  })
  .on('complete', function () {
    console.log('Fastest is ' + this.filter('fastest').map('name'))
  })
  // Run async
  .run({ async: true })

Result:

JSON Store (size 20) - Read x 46,625 ops/sec ±0.97% (87 runs sampled)
Array Store (size 20) - Read x 127 ops/sec ±54.01% (8 runs sampled)
JSON Store (size 20) - Write x 23.59 ops/sec ±0.96% (42 runs sampled)
Array Store (size 20) - Write x 2,608 ops/sec ±4.27% (82 runs sampled)
JSON Store (size 100) - Read x 10,622 ops/sec ±0.31% (90 runs sampled)
Array Store (size 100) - Read x 17.85 ops/sec ±83.33% (5 runs sampled)
JSON Store (size 100) - Write x 3.75 ops/sec ±0.85% (14 runs sampled)
Array Store (size 100) - Write x 11.07 ops/sec ±5.38% (31 runs sampled)
JSON Store (size 1000) - Read x 1,058 ops/sec ±1.37% (90 runs sampled)
Array Store (size 1000) - Read x 12.46 ops/sec ±67.78% (6 runs sampled)
JSON Store (size 1000) - Write x 2.34 ops/sec ±3.62% (10 runs sampled)
Array Store (size 1000) - Write x 4.96 ops/sec ±1.99% (16 runs sampled)
JSON Store (size 10000) - Read x 93.73 ops/sec ±1.44% (77 runs sampled)
Array Store (size 10000) - Read x 5.16 ops/sec ±31.77% (12 runs sampled)
JSON Store (size 10000) - Write x 1.18 ops/sec ±2.89% (7 runs sampled)
Array Store (size 10000) - Write x 2.15 ops/sec ±9.04% (10 runs sampled)
tylerlong commented 5 days ago

I am working on version 2.0 which is a complete rewrite. I will check this issue later.

tylerlong commented 5 days ago

With version 1.x there are some workarounds. For example, before you manage an obj, you may iterate all of Object.values(obj) and exclude all non-primitive props. Then the result is the same as only monitor the top level changes.

In your example:

import { exclude } from 'manate';

const testData = generateTestData(size)
exclude(testData);
...
tylerlong commented 5 days ago

In your test cases, you seem to invoke manage again and again on the same data, which is very uncommon. In reality, most people only manage once, and play with the managed object afterwards. It is a known limitation that setup the managed object is expensive. But again, I am working on version 2.0. You are welcome to test it again.