Skip to content

Commit

Permalink
recorder: Collect transcription download perf
Browse files Browse the repository at this point in the history
Collect transcription model download perf when the download event is
triggered from download buttons.

Add PerfLoggerBase so that we can have multiple
`transcriptionModelDownload` events for different transcription language
at the same time.

Bug: b:367263595
Test: manually - check chrome://metrics-internals/structured
Change-Id: Ic6d69910bb828d7d2e590593ada92d518623a8fd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6038989
Reviewed-by: Pi-Hsun Shih <pihsun@chromium.org>
Reviewed-by: Andrew Bregger <andrewbregger@google.com>
Commit-Queue: Jennifer Ling <hsuanling@google.com>
Cr-Commit-Position: refs/heads/main@{#1389136}
  • Loading branch information
hsuanling authored and pull[bot] committed Nov 29, 2024
1 parent 29d3a91 commit 1038996
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 28 deletions.
36 changes: 34 additions & 2 deletions ash/webui/recorder_app_ui/resources/core/events_sender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,57 @@ export interface ChangePlaybackVolumeParams {
}

interface DurationOnlyPerf {
kind: 'appStart'|'summaryModelDownload'|'transcriptionModelDownload';
kind: 'appStart'|'summaryModelDownload';
}

interface TranscriptionModelDownloadPerf {
// LanguageCode is included in `kind` so that perf of each language can be
// collected independently.
kind: `transcriptionModelDownload-${LanguageCode}`;
transcriptionLocale: LanguageCode;
}

interface RecordPerf {
// Audio duration in milliseconds.
audioDuration: number;
kind: 'record';
wordCount: number;
}

interface ModelProcessPerf {
kind: 'summary'|'titleSuggestion';
wordCount: number;
}

interface ExportPerf {
kind: 'export';
// Recording size in bytes.
recordingSize: number;
}

export type PerfEvent = DurationOnlyPerf|ExportPerf|ModelProcessPerf|RecordPerf;
export type PerfEvent = DurationOnlyPerf|ExportPerf|ModelProcessPerf|RecordPerf|
TranscriptionModelDownloadPerf;

/**
* Creates `TranscriptionModelDownloadPerf` for given language.
*/
export function createTranscriptionModelDownloadPerf(
transcriptionLocale: LanguageCode,
): TranscriptionModelDownloadPerf {
return {
kind: `transcriptionModelDownload-${transcriptionLocale}`,
transcriptionLocale,
};
}

/**
* Checks if a `PerfEvent` is a `TranscriptionModelDownloadPerf`.
*/
export function isTranscriptionModelDownloadPerf(
perfEvent: PerfEvent,
): perfEvent is TranscriptionModelDownloadPerf {
return perfEvent.kind.startsWith('transcriptionModelDownload-');
}

export abstract class EventsSender {
abstract sendStartSessionEvent(params: StartSessionEventParams): void;
Expand Down
11 changes: 0 additions & 11 deletions ash/webui/recorder_app_ui/resources/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {DataDir} from './data_dir.js';
import {initContext} from './lit/context.js';
import {MicrophoneManager} from './microphone_manager.js';
import {PlatformHandler} from './platform_handler.js';
import {effect} from './reactive/signal.js';
import {RecordingDataManager} from './recording_data_manager.js';
import {installRouter} from './state/route.js';
import {init as initSettings} from './state/settings.js';
Expand Down Expand Up @@ -40,14 +39,4 @@ export async function init(platformHandler: PlatformHandler): Promise<void> {
recordingDataManager,
platformHandler,
});
effect(() => {
const state = platformHandler.summaryModelLoader.state.value;
const summaryEventType = 'summaryModelDownload';
if (state.kind === 'installed') {
// Records perf event only if the download has been initiated from UI.
if (platformHandler.perfLogger.hasPerfEvent(summaryEventType)) {
platformHandler.perfLogger.finish(summaryEventType);
}
}
});
}
16 changes: 14 additions & 2 deletions ash/webui/recorder_app_ui/resources/core/perf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,22 @@ export class PerfLogger {
});
}

hasPerfEvent(eventType: EventType): boolean {
return this.perfEventMap.has(eventType);
/**
* Finishes the event if it has started.
*
* This avoids `finish` error when we only record `eventType` initiated from
* particular sources, e.g. UI.
*/
tryFinish(eventType: EventType): void {
if (!this.perfEventMap.has(eventType)) {
return;
}
this.finish(eventType);
}

/**
* Terminates and sends the perf event.
*/
finish(eventType: EventType): void {
const eventValue = this.perfEventMap.get(eventType);
if (eventValue === undefined) {
Expand Down
36 changes: 34 additions & 2 deletions ash/webui/recorder_app_ui/resources/core/platform_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import {EventsSender} from './events_sender.js';
import {
createTranscriptionModelDownloadPerf,
EventsSender,
} from './events_sender.js';
import {NoArgStringName} from './i18n.js';
import {InternalMicInfo} from './microphone_manager.js';
import {ModelLoader, ModelState} from './on_device_model/types.js';
import {PerfLogger} from './perf.js';
import {ReadonlySignal, Signal} from './reactive/signal.js';
import {effect, ReadonlySignal, Signal} from './reactive/signal.js';
import {LangPackInfo, LanguageCode} from './soda/language_info.js';
import {SodaSession} from './soda/types.js';
import {settings} from './state/settings.js';
Expand Down Expand Up @@ -222,4 +225,33 @@ export abstract class PlatformHandler {
* Performance logger to measure performance.
*/
abstract readonly perfLogger: PerfLogger;

/**
* Adds model state watchers for perf events.
*/
initPerfEventWatchers(): void {
// Watcher for summarization model download.
effect(() => {
const state = this.summaryModelLoader.state.value;
const summaryEventType = 'summaryModelDownload';
if (state.kind === 'installed') {
// Records perf event only if the download has been initiated from UI.
this.perfLogger.tryFinish(summaryEventType);
}
});

// Watchers for transcription model download.
const languageList = this.getLangPackList();
for (const language of languageList) {
effect(() => {
const state = this.getSodaState(language.languageCode).value;
if (state.kind === 'installed') {
// Records perf event only if the download has been initiated from UI.
this.perfLogger.tryFinish(
createTranscriptionModelDownloadPerf(language.languageCode).kind,
);
}
});
}
}
}
21 changes: 18 additions & 3 deletions ash/webui/recorder_app_ui/resources/core/state/transcription.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {createTranscriptionModelDownloadPerf} from '../events_sender.js';
import {usePlatformHandler} from '../lit/context.js';
import {LanguageCode} from '../soda/language_info.js';
import {assertExhaustive} from '../utils/assert.js';
Expand All @@ -20,8 +21,22 @@ export function disableTranscription(firstTime = false): void {
});
}

function installSoda(language: LanguageCode) {
const platformHandler = usePlatformHandler();
// Records download events initiated from UI download buttons.
if (platformHandler.getSodaState(language).value.kind === 'notInstalled') {
platformHandler.perfLogger.start(
createTranscriptionModelDownloadPerf(language),
);
}
// TODO: b/375306309 - Install only if the state is `notInstalled` after the
// `OnSodaUninstalled` event is implemented and there's no inconsistent soda
// state.
void platformHandler.installSoda(language);
}

/**
* Enables transcription.
* Wrapper that installs Soda and starts download perf event.
*/
export function enableTranscriptionSkipConsentCheck(): void {
settings.mutate((s) => {
Expand All @@ -30,7 +45,7 @@ export function enableTranscriptionSkipConsentCheck(): void {
const platformHandler = usePlatformHandler();
const selectedLanguage = platformHandler.getSelectedLanguage();
if (selectedLanguage !== null) {
void platformHandler.installSoda(selectedLanguage);
installSoda(selectedLanguage);
}
}

Expand All @@ -41,7 +56,7 @@ export function setTranscriptionLanguage(language: LanguageCode): void {
settings.mutate((s) => {
s.transcriptionLanguage = language;
});
void usePlatformHandler().installSoda(language);
installSoda(language);
}

/**
Expand Down
1 change: 0 additions & 1 deletion ash/webui/recorder_app_ui/resources/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@ document.addEventListener('DOMContentLoaded', async () => {
await init(platformHandler);
// Initialize platform.
await platformHandler.init();

document.body.appendChild(new RecorderApp());
});
2 changes: 2 additions & 0 deletions ash/webui/recorder_app_ui/resources/platforms/dev/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ export class PlatformHandler extends PlatformHandlerBase {
isGenAiSupported: true,
isSpeakerLabelSupported: true,
});

this.initPerfEventWatchers();
}

override getLangPackList(): readonly LangPackInfo[] {
Expand Down
2 changes: 2 additions & 0 deletions ash/webui/recorder_app_ui/resources/platforms/swa/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ export class PlatformHandler extends PlatformHandlerBase {

await this.summaryModelLoader.init();
await this.titleSuggestionModelLoader.init();

this.initPerfEventWatchers();
}

override getLangPackList = lazyInit((): readonly LangPackInfo[] => {
Expand Down
23 changes: 16 additions & 7 deletions ash/webui/recorder_app_ui/resources/platforms/swa/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
EventsSender as EventsSenderBase,
ExportEventParams,
FeedbackEventParams,
isTranscriptionModelDownloadPerf,
OnboardEventParams,
PerfEvent,
RecordEventParams,
Expand Down Expand Up @@ -440,6 +441,12 @@ export class EventsSender extends EventsSenderBase {
}

override sendPerfEvent(event: PerfEvent, duration: number): void {
if (isTranscriptionModelDownloadPerf(event)) {
return this.sendTranscriptionModelDownloadPerf(
duration,
event.transcriptionLocale,
);
}
const {kind} = event;
switch (kind) {
case 'appStart':
Expand All @@ -458,9 +465,6 @@ export class EventsSender extends EventsSenderBase {
return this.sendSummaryModelDownloadPerf(duration);
case 'titleSuggestion':
return this.sendTitleSuggestionPerf(duration, event.wordCount);
case 'transcriptionModelDownload':
// TODO: b/327538356 - Collect soda download perf.
return this.sendTranscriptionModelDownloadPerf(duration);
default:
assertExhaustive(kind);
}
Expand All @@ -474,10 +478,15 @@ export class EventsSender extends EventsSenderBase {
record(event);
}

private sendTranscriptionModelDownloadPerf(duration: number): void {
const event = new CrOSEvents_RecorderApp_TranscriptionModelDownloadPerf()
.setDuration(BigInt(duration))
.build();
private sendTranscriptionModelDownloadPerf(
duration: number,
language: LanguageCode,
): void {
const event =
new CrOSEvents_RecorderApp_TranscriptionModelDownloadPerf()
.setDuration(BigInt(duration))
.setTranscriptionLocale(convertTranscriptionLocaleType(language))
.build();

record(event);
}
Expand Down
5 changes: 5 additions & 0 deletions tools/metrics/structured/sync/structured.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3002,6 +3002,11 @@
The duration of transcription model download in milliseconds.
</summary>
</metric>
<metric name="TranscriptionLocale" type="RecorderAppTranscriptionLocale">
<summary>
The locale for which to download the transcription model.
</summary>
</metric>
</event>

<event name="RecorderApp.SummaryModelDownloadPerf">
Expand Down

0 comments on commit 1038996

Please sign in to comment.