mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #10235 from overleaf/td-cm6-lag-metrics
Add CM6 typing lag metrics GitOrigin-RevId: 7068647a28980d888ecb5a0203f0a3563ce4c713
This commit is contained in:
parent
2bb8bd6f14
commit
a320982f79
2 changed files with 83 additions and 8 deletions
|
@ -280,6 +280,11 @@ If the project has been renamed please look in your project list for a new proje
|
||||||
'Grammarly',
|
'Grammarly',
|
||||||
'SessionLength',
|
'SessionLength',
|
||||||
'Memory',
|
'Memory',
|
||||||
|
'Lags',
|
||||||
|
'NonLags',
|
||||||
|
'LongestLag',
|
||||||
|
'MeanLagsPerMeasure',
|
||||||
|
'MeanKeypressesPerMeasure',
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const prop of perfProps) {
|
for (const prop of perfProps) {
|
||||||
|
|
|
@ -5,19 +5,31 @@ import grammarlyExtensionPresent from '../shared/utils/grammarly'
|
||||||
|
|
||||||
const TIMER_START_NAME = 'CM6-BeforeUpdate'
|
const TIMER_START_NAME = 'CM6-BeforeUpdate'
|
||||||
const TIMER_END_NAME = 'CM6-AfterUpdate'
|
const TIMER_END_NAME = 'CM6-AfterUpdate'
|
||||||
|
const TIMER_DOM_UPDATE_NAME = 'CM6-DomUpdate'
|
||||||
const TIMER_MEASURE_NAME = 'CM6-Update'
|
const TIMER_MEASURE_NAME = 'CM6-Update'
|
||||||
|
|
||||||
let latestDocLength = 0
|
let latestDocLength = 0
|
||||||
const sessionStart = Date.now()
|
const sessionStart = Date.now()
|
||||||
|
|
||||||
let performanceMeasureOptionsSupport = false
|
let performanceOptionsSupport = false
|
||||||
|
|
||||||
// Check that performance.measure accepts an options object
|
// Check that performance.mark and performance.measure accept an options object
|
||||||
try {
|
try {
|
||||||
const testMeasureName = 'featureTest'
|
const testMarkName = 'featureTestMark'
|
||||||
performance.measure(testMeasureName, { start: performance.now() })
|
performance.mark(testMarkName, {
|
||||||
|
startTime: performance.now(),
|
||||||
|
detail: { test: 1 },
|
||||||
|
})
|
||||||
|
performance.clearMarks(testMarkName)
|
||||||
|
|
||||||
|
const testMeasureName = 'featureTestMeasure'
|
||||||
|
performance.measure(testMeasureName, {
|
||||||
|
start: performance.now(),
|
||||||
|
detail: { test: 1 },
|
||||||
|
})
|
||||||
performance.clearMeasures(testMeasureName)
|
performance.clearMeasures(testMeasureName)
|
||||||
performanceMeasureOptionsSupport = true
|
|
||||||
|
performanceOptionsSupport = true
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
let performanceMemorySupport = false
|
let performanceMemorySupport = false
|
||||||
|
@ -40,15 +52,26 @@ function isInputOrDelete(userEventType: string | undefined) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "keypress" is not strictly accurate; what we really mean is a user-initiated
|
||||||
|
// event that either inserts or deletes exactly one character. This corresponds
|
||||||
|
// to CM6 user event types input.type, delete.forward or delete.backward
|
||||||
|
function isKeypress(userEventType: string | undefined) {
|
||||||
|
return (
|
||||||
|
!!userEventType &&
|
||||||
|
['input.type', 'delete.forward', 'delete.backward'].includes(userEventType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function timedDispatch() {
|
export function timedDispatch() {
|
||||||
let userEventsSinceDomUpdateCount = 0
|
let userEventsSinceDomUpdateCount = 0
|
||||||
|
let keypressesSinceDomUpdateCount = 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
view: EditorView,
|
view: EditorView,
|
||||||
tr: Transaction,
|
tr: Transaction,
|
||||||
dispatchFn: (tr: Transaction) => void
|
dispatchFn: (tr: Transaction) => void
|
||||||
) => {
|
) => {
|
||||||
if (!performanceMeasureOptionsSupport) {
|
if (!performanceOptionsSupport) {
|
||||||
dispatchFn(tr)
|
dispatchFn(tr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -64,6 +87,10 @@ export function timedDispatch() {
|
||||||
if (isInputOrDelete(userEventType)) {
|
if (isInputOrDelete(userEventType)) {
|
||||||
++userEventsSinceDomUpdateCount
|
++userEventsSinceDomUpdateCount
|
||||||
|
|
||||||
|
if (isKeypress(userEventType)) {
|
||||||
|
++keypressesSinceDomUpdateCount
|
||||||
|
}
|
||||||
|
|
||||||
performance.measure(TIMER_MEASURE_NAME, {
|
performance.measure(TIMER_MEASURE_NAME, {
|
||||||
start: TIMER_START_NAME,
|
start: TIMER_START_NAME,
|
||||||
end: TIMER_END_NAME,
|
end: TIMER_END_NAME,
|
||||||
|
@ -75,7 +102,11 @@ export function timedDispatch() {
|
||||||
view.requestMeasure({
|
view.requestMeasure({
|
||||||
key: 'inputEventCounter',
|
key: 'inputEventCounter',
|
||||||
read() {
|
read() {
|
||||||
|
performance.mark(TIMER_DOM_UPDATE_NAME, {
|
||||||
|
detail: { keypressesSinceDomUpdateCount },
|
||||||
|
})
|
||||||
userEventsSinceDomUpdateCount = 0
|
userEventsSinceDomUpdateCount = 0
|
||||||
|
keypressesSinceDomUpdateCount = 0
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -120,6 +151,10 @@ export function reportCM6Perf() {
|
||||||
'measure'
|
'measure'
|
||||||
) as PerformanceMeasure[]
|
) as PerformanceMeasure[]
|
||||||
|
|
||||||
|
performance.clearMeasures(TIMER_MEASURE_NAME)
|
||||||
|
performance.clearMarks(TIMER_START_NAME)
|
||||||
|
performance.clearMarks(TIMER_END_NAME)
|
||||||
|
|
||||||
const inputEvents = cm6Entries.filter(({ detail }) =>
|
const inputEvents = cm6Entries.filter(({ detail }) =>
|
||||||
isInputOrDelete(detail.userEventType)
|
isInputOrDelete(detail.userEventType)
|
||||||
)
|
)
|
||||||
|
@ -141,10 +176,40 @@ export function reportCM6Perf() {
|
||||||
const grammarly = grammarlyExtensionPresent()
|
const grammarly = grammarlyExtensionPresent()
|
||||||
const sessionLength = Math.floor((Date.now() - sessionStart) / 1000) // In seconds
|
const sessionLength = Math.floor((Date.now() - sessionStart) / 1000) // In seconds
|
||||||
|
|
||||||
performance.clearMeasures(TIMER_MEASURE_NAME)
|
|
||||||
|
|
||||||
const memory = performanceMemorySupport ? measureMemoryUsage() : null
|
const memory = performanceMemorySupport ? measureMemoryUsage() : null
|
||||||
|
|
||||||
|
// Get entries for keypress counts between DOM updates
|
||||||
|
const domUpdateEntries = performance.getEntriesByName(
|
||||||
|
TIMER_DOM_UPDATE_NAME,
|
||||||
|
'mark'
|
||||||
|
) as PerformanceMeasure[]
|
||||||
|
|
||||||
|
performance.clearMarks(TIMER_DOM_UPDATE_NAME)
|
||||||
|
|
||||||
|
let lags = 0
|
||||||
|
let nonLags = 0
|
||||||
|
let longestLag = 0
|
||||||
|
let totalKeypressCount = 0
|
||||||
|
|
||||||
|
for (const entry of domUpdateEntries) {
|
||||||
|
const keypressCount = entry.detail.keypressesSinceDomUpdateCount
|
||||||
|
if (keypressCount === 1) {
|
||||||
|
++nonLags
|
||||||
|
} else if (keypressCount > 1) {
|
||||||
|
++lags
|
||||||
|
}
|
||||||
|
if (keypressCount > longestLag) {
|
||||||
|
longestLag = keypressCount
|
||||||
|
}
|
||||||
|
totalKeypressCount += keypressCount
|
||||||
|
}
|
||||||
|
|
||||||
|
const meanLagsPerMeasure = round(lags / (lags + nonLags), 4)
|
||||||
|
const meanKeypressesPerMeasure = round(
|
||||||
|
totalKeypressCount / (lags + nonLags),
|
||||||
|
4
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
max,
|
max,
|
||||||
mean,
|
mean,
|
||||||
|
@ -156,6 +221,11 @@ export function reportCM6Perf() {
|
||||||
grammarly,
|
grammarly,
|
||||||
sessionLength,
|
sessionLength,
|
||||||
memory,
|
memory,
|
||||||
|
lags,
|
||||||
|
nonLags,
|
||||||
|
longestLag,
|
||||||
|
meanLagsPerMeasure,
|
||||||
|
meanKeypressesPerMeasure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue