Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy load interactive or static variants of a component individually, rather than loading both variants regardless. This change will improve performance for many applications. #5215

Merged
merged 16 commits into from Aug 15, 2023
Merged
33 changes: 33 additions & 0 deletions .changeset/forty-cases-cough.md
@@ -0,0 +1,33 @@
---
"@gradio/accordion": patch
"@gradio/annotatedimage": patch
"@gradio/app": patch
"@gradio/audio": patch
"@gradio/box": patch
"@gradio/chatbot": patch
"@gradio/checkbox": patch
"@gradio/checkboxgroup": patch
"@gradio/code": patch
"@gradio/colorpicker": patch
"@gradio/dataframe": patch
"@gradio/dropdown": patch
"@gradio/file": patch
"@gradio/gallery": patch
"@gradio/highlightedtext": patch
"@gradio/html": patch
"@gradio/image": patch
"@gradio/json": patch
"@gradio/label": patch
"@gradio/markdown": patch
"@gradio/model3d": patch
"@gradio/number": patch
"@gradio/plot": patch
"@gradio/radio": patch
"@gradio/slider": patch
"@gradio/statustracker": patch
"@gradio/timeseries": patch
"@gradio/video": patch
"gradio": patch
---

feat:Lazy load interactive or static variants of a component individually, rather than loading both variants regardless. This change will improve performance for many applications.
3 changes: 2 additions & 1 deletion .config/.prettierignore
Expand Up @@ -22,4 +22,5 @@
**/storybook-static/**
**/.vscode/**
sweep.yaml
**/.vercel/**
**/.vercel/**
**/build/**
2 changes: 1 addition & 1 deletion js/accordion/static/StaticAccordion.svelte
Expand Up @@ -2,7 +2,7 @@
import Accordion from "./Accordion.svelte";
import { Block } from "@gradio/atoms";
import { StatusTracker } from "@gradio/statustracker";
import type { LoadingStatus } from "@gradio/statustracker/types";
import type { LoadingStatus } from "@gradio/statustracker";

import Column from "@gradio/column";

Expand Down
2 changes: 1 addition & 1 deletion js/annotatedimage/static/AnnotatedImage.svelte
Expand Up @@ -3,7 +3,7 @@
import { Block, BlockLabel, Empty } from "@gradio/atoms";
import { Image } from "@gradio/icons";
import { StatusTracker } from "@gradio/statustracker";
import type { LoadingStatus } from "@gradio/statustracker/types";
import type { LoadingStatus } from "@gradio/statustracker";
import { type FileData, normalise_file } from "@gradio/upload";
import type { SelectData } from "@gradio/utils";

Expand Down
99 changes: 77 additions & 22 deletions js/app/src/Blocks.svelte
Expand Up @@ -50,7 +50,7 @@
let rootNode: ComponentMeta = {
id: layout.id,
type: "column",
props: {},
props: { mode: "static" },
has_modes: false,
instance: {} as ComponentMeta["instance"],
component: {} as ComponentMeta["component"]
Expand Down Expand Up @@ -134,59 +134,82 @@
);

type LoadedComponent = {
Component: ComponentMeta["component"];
modes?: string[];
document?: (arg0: Record<string, unknown>) => Documentation;
default: ComponentMeta["component"];
};

async function load_component<T extends ComponentMeta["type"]>(
name: T
name: T,
mode: ComponentMeta["props"]["mode"]
): Promise<{
name: T;
component: LoadedComponent;
}> {
try {
const c = await component_map[name]();
//@ts-ignore
const c = await component_map[name][mode]();
return {
name,
component: c as LoadedComponent
};
} catch (e) {
console.error(`failed to load: ${name}`);
console.error(e);
throw e;
if (mode === "interactive") {
try {
const c = await component_map[name]["static"]();
return {
name,
component: c as LoadedComponent
};
} catch (e) {
console.error(`failed to load: ${name}`);
console.error(e);
throw e;
}
} else {
console.error(`failed to load: ${name}`);
console.error(e);
throw e;
}
}
}

const component_set = new Set<
Promise<{ name: ComponentMeta["type"]; component: LoadedComponent }>
>();

const _component_map = new Map<
ComponentMeta["type"],
`${ComponentMeta["type"]}_${ComponentMeta["props"]["mode"]}`,
Promise<{ name: ComponentMeta["type"]; component: LoadedComponent }>
>();
const _type_for_id = new Map<number, ComponentMeta["props"]["mode"]>();

async function walk_layout(node: LayoutNode): Promise<void> {
let instance = instance_map[node.id];
const _component = (await _component_map.get(instance.type))!.component;
instance.component = _component.Component;
if (_component.document) {
instance.documentation = _component.document(instance.props);
}
if (_component.modes && _component.modes.length > 1) {
instance.has_modes = true;
}
const _component = (await _component_map.get(
`${instance.type}_${_type_for_id.get(node.id) || "static"}`
))!.component;
instance.component = _component.default;

if (node.children) {
instance.children = node.children.map((v) => instance_map[v.id]);
await Promise.all(node.children.map((v) => walk_layout(v)));
}
}

components.forEach(async (c) => {
const _c = load_component(c.type);
components.forEach((c) => {
if ((c.props as any).interactive === false) {
(c.props as any).mode = "static";
} else if ((c.props as any).interactive === true) {
(c.props as any).mode = "interactive";
} else if (dynamic_ids.has(c.id)) {
(c.props as any).mode = "interactive";
} else {
(c.props as any).mode = "static";
}
_type_for_id.set(c.id, c.props.mode);

const _c = load_component(c.type, c.props.mode);
component_set.add(_c);
_component_map.set(c.type, _c);
_component_map.set(`${c.type}_${c.props.mode}`, _c);
});

export let ready = false;
Expand All @@ -200,6 +223,32 @@
});
});

async function update_interactive_mode(
instance: ComponentMeta,
mode: "dynamic" | "interactive" | "static"
): Promise<void> {
let new_mode: "interactive" | "static" =
mode === "dynamic" ? "interactive" : mode;

if (instance.props.mode === new_mode) return;

instance.props.mode = new_mode;
const _c = load_component(instance.type, instance.props.mode);
component_set.add(_c);
_component_map.set(
`${instance.type}_${instance.props.mode}`,
_c as Promise<{
name: ComponentMeta["type"];
component: LoadedComponent;
}>
);

_c.then((c) => {
instance.component = c.component.default;
rootNode = rootNode;
});
}

function handle_update(data: any, fn_index: number): void {
const outputs = dependencies[fn_index].outputs;
data?.forEach((value: any, i: number) => {
Expand All @@ -214,6 +263,12 @@
if (update_key === "__type__") {
continue;
} else {
if (update_key === "mode") {
update_interactive_mode(
output,
update_value as "dynamic" | "static"
);
}
output.props[update_key] = update_value;
}
}
Expand All @@ -232,6 +287,7 @@
val: any
): void {
if (!obj?.props) {
// @ts-ignore
obj.props = {};
}
obj.props[prop] = val;
Expand Down Expand Up @@ -570,7 +626,6 @@
<div class="contain" style:flex-grow={app_mode ? "1" : "auto"}>
{#if ready}
<Render
has_modes={rootNode.has_modes}
component={rootNode.component}
id={rootNode.id}
props={rootNode.props}
Expand Down
2 changes: 1 addition & 1 deletion js/app/src/Login.svelte
Expand Up @@ -2,7 +2,7 @@
import Form from "@gradio/form";
import Textbox from "@gradio/textbox";
import { BaseButton } from "@gradio/button/static";
import { Component as Column } from "./components/Column";
import Column from "@gradio/column";
export let root: string;
export let auth_message: string | null;
export let app_mode: boolean;
Expand Down
13 changes: 0 additions & 13 deletions js/app/src/Render.svelte
Expand Up @@ -12,25 +12,12 @@

export let children: ComponentMeta["children"];
export let dynamic_ids: Set<number>;
export let has_modes: boolean | undefined;
export let parent: string | null = null;
export let target: HTMLElement;
export let theme_mode: ThemeMode;

const dispatch = createEventDispatcher<{ mount: number; destroy: number }>();

if (has_modes) {
if ((props as any).interactive === false) {
(props as any).mode = "static";
} else if ((props as any).interactive === true) {
(props as any).mode = "dynamic";
} else if (dynamic_ids.has(id)) {
(props as any).mode = "dynamic";
} else {
(props as any).mode = "static";
}
}

onMount(() => {
dispatch("mount", id);

Expand Down
2 changes: 0 additions & 2 deletions js/app/src/components/Accordion/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/AnnotatedImage/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Audio/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Box/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Button/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Chatbot/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Checkbox/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/CheckboxGroup/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Code/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/ColorPicker/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Column/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/DataFrame/index.ts

This file was deleted.

3 changes: 1 addition & 2 deletions js/app/src/components/Dataset/index.ts
@@ -1,2 +1 @@
export { default as Component } from "./Dataset.svelte";
export const modes = ["dynamic"];
export { default } from "./Dataset.svelte";
2 changes: 0 additions & 2 deletions js/app/src/components/Dropdown/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/File/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Form/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Gallery/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Group/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/HTML/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/HighlightedText/index.ts

This file was deleted.

3 changes: 0 additions & 3 deletions js/app/src/components/Image/index.ts

This file was deleted.

3 changes: 1 addition & 2 deletions js/app/src/components/Interpretation/index.ts
@@ -1,2 +1 @@
export { default as Component } from "./Interpretation.svelte";
export const modes = ["dynamic"];
export { default } from "./Interpretation.svelte";
2 changes: 0 additions & 2 deletions js/app/src/components/Json/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Label/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Markdown/index.ts

This file was deleted.

3 changes: 0 additions & 3 deletions js/app/src/components/Model3D/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Number/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Plot/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Radio/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Row/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Slider/index.ts

This file was deleted.

3 changes: 1 addition & 2 deletions js/app/src/components/State/index.ts
@@ -1,2 +1 @@
export { default as Component } from "@gradio/state";
export const modes = ["static"];
export { default } from "@gradio/state";
2 changes: 0 additions & 2 deletions js/app/src/components/StatusTracker/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/TabItem/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Tabs/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Textbox/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/TimeSeries/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/UploadButton/index.ts

This file was deleted.

2 changes: 0 additions & 2 deletions js/app/src/components/Video/index.ts

This file was deleted.