Emoji Rendering Efficiency (#35568)
This commit is contained in:
parent
0e249cba4b
commit
6bca52453a
23 changed files with 954 additions and 333 deletions
78
app/javascript/mastodon/utils/__tests__/cache.test.ts
Normal file
78
app/javascript/mastodon/utils/__tests__/cache.test.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import { createLimitedCache } from '../cache';
|
||||
|
||||
describe('createCache', () => {
|
||||
test('returns expected methods', () => {
|
||||
const actual = createLimitedCache();
|
||||
expect(actual).toBeTypeOf('object');
|
||||
expect(actual).toHaveProperty('get');
|
||||
expect(actual).toHaveProperty('has');
|
||||
expect(actual).toHaveProperty('delete');
|
||||
expect(actual).toHaveProperty('set');
|
||||
});
|
||||
|
||||
test('caches values provided to it', () => {
|
||||
const cache = createLimitedCache();
|
||||
cache.set('test', 'result');
|
||||
expect(cache.get('test')).toBe('result');
|
||||
});
|
||||
|
||||
test('has returns expected values', () => {
|
||||
const cache = createLimitedCache();
|
||||
cache.set('test', 'result');
|
||||
expect(cache.has('test')).toBeTruthy();
|
||||
expect(cache.has('not found')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('updates a value if keys are the same', () => {
|
||||
const cache = createLimitedCache();
|
||||
cache.set('test1', 1);
|
||||
cache.set('test1', 2);
|
||||
expect(cache.get('test1')).toBe(2);
|
||||
});
|
||||
|
||||
test('delete removes an item', () => {
|
||||
const cache = createLimitedCache();
|
||||
cache.set('test', 'result');
|
||||
expect(cache.has('test')).toBeTruthy();
|
||||
cache.delete('test');
|
||||
expect(cache.has('test')).toBeFalsy();
|
||||
expect(cache.get('test')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('removes oldest item cached if it exceeds a set size', () => {
|
||||
const cache = createLimitedCache({ maxSize: 1 });
|
||||
cache.set('test1', 1);
|
||||
cache.set('test2', 2);
|
||||
expect(cache.get('test1')).toBeUndefined();
|
||||
expect(cache.get('test2')).toBe(2);
|
||||
});
|
||||
|
||||
test('retrieving a value bumps up last access', () => {
|
||||
const cache = createLimitedCache({ maxSize: 2 });
|
||||
cache.set('test1', 1);
|
||||
cache.set('test2', 2);
|
||||
expect(cache.get('test1')).toBe(1);
|
||||
cache.set('test3', 3);
|
||||
expect(cache.get('test1')).toBe(1);
|
||||
expect(cache.get('test2')).toBeUndefined();
|
||||
expect(cache.get('test3')).toBe(3);
|
||||
});
|
||||
|
||||
test('logs when cache is added to and removed', () => {
|
||||
const log = vi.fn();
|
||||
const cache = createLimitedCache({ maxSize: 1, log });
|
||||
cache.set('test1', 1);
|
||||
expect(log).toHaveBeenLastCalledWith(
|
||||
'Added %s to cache, now size %d',
|
||||
'test1',
|
||||
1,
|
||||
);
|
||||
cache.set('test2', 1);
|
||||
expect(log).toHaveBeenLastCalledWith(
|
||||
'Added %s and deleted %s from cache, now size %d',
|
||||
'test2',
|
||||
'test1',
|
||||
1,
|
||||
);
|
||||
});
|
||||
});
|
||||
60
app/javascript/mastodon/utils/cache.ts
Normal file
60
app/javascript/mastodon/utils/cache.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
export interface LimitedCache<CacheKey, CacheValue> {
|
||||
has: (key: CacheKey) => boolean;
|
||||
get: (key: CacheKey) => CacheValue | undefined;
|
||||
delete: (key: CacheKey) => void;
|
||||
set: (key: CacheKey, value: CacheValue) => void;
|
||||
clear: () => void;
|
||||
}
|
||||
|
||||
interface LimitedCacheArguments {
|
||||
maxSize?: number;
|
||||
log?: (...args: unknown[]) => void;
|
||||
}
|
||||
|
||||
export function createLimitedCache<CacheValue, CacheKey = string>({
|
||||
maxSize = 100,
|
||||
log = () => null,
|
||||
}: LimitedCacheArguments = {}): LimitedCache<CacheKey, CacheValue> {
|
||||
const cacheMap = new Map<CacheKey, CacheValue>();
|
||||
const cacheKeys = new Set<CacheKey>();
|
||||
|
||||
function touchKey(key: CacheKey) {
|
||||
if (cacheKeys.has(key)) {
|
||||
cacheKeys.delete(key);
|
||||
}
|
||||
cacheKeys.add(key);
|
||||
}
|
||||
|
||||
return {
|
||||
has: (key) => cacheMap.has(key),
|
||||
get: (key) => {
|
||||
if (cacheMap.has(key)) {
|
||||
touchKey(key);
|
||||
}
|
||||
return cacheMap.get(key);
|
||||
},
|
||||
delete: (key) => cacheMap.delete(key) && cacheKeys.delete(key),
|
||||
set: (key, value) => {
|
||||
cacheMap.set(key, value);
|
||||
touchKey(key);
|
||||
|
||||
const lastKey = cacheKeys.values().toArray().shift();
|
||||
if (cacheMap.size > maxSize && lastKey) {
|
||||
cacheMap.delete(lastKey);
|
||||
cacheKeys.delete(lastKey);
|
||||
log(
|
||||
'Added %s and deleted %s from cache, now size %d',
|
||||
key,
|
||||
lastKey,
|
||||
cacheMap.size,
|
||||
);
|
||||
} else {
|
||||
log('Added %s to cache, now size %d', key, cacheMap.size);
|
||||
}
|
||||
},
|
||||
clear: () => {
|
||||
cacheMap.clear();
|
||||
cacheKeys.clear();
|
||||
},
|
||||
};
|
||||
}
|
||||
19
app/javascript/mastodon/utils/performance.ts
Normal file
19
app/javascript/mastodon/utils/performance.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// Tools for performance debugging, only enabled in development mode.
|
||||
// Open up Chrome Dev Tools, then Timeline, then User Timing to see output.
|
||||
|
||||
import * as marky from 'marky';
|
||||
|
||||
import { isDevelopment } from './environment';
|
||||
|
||||
export function start(name: string) {
|
||||
if (isDevelopment()) {
|
||||
marky.mark(name);
|
||||
}
|
||||
}
|
||||
|
||||
export function stop(name: string) {
|
||||
if (isDevelopment()) {
|
||||
marky.stop(name);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue