@@ -911,37 +911,44 @@ export class SliceTrack<T extends RowSchema> implements TrackRenderer {
911911 const depths = new Uint16Array ( count ) ;
912912 const instants = new Array < Instant < T > > ( count ) ;
913913
914- const it = queryResult . iter ( {
914+ // Bulk-decode all columns in one pass (much faster than per-row iter() for
915+ // large tracks). The fixed columns come back as typed arrays; the schema
916+ // columns are read per-row to build the `row` object the callbacks need.
917+ const cols = queryResult . decodeColumns ( {
915918 __id : NUM ,
916919 __ts : NUM ,
917920 __count : NUM ,
918921 __depth : NUM ,
919922 ...dataset . schema ,
920923 } ) ;
924+ const idCol = cols . __id ;
925+ const tsCol = cols . __ts ;
926+ const countCol = cols . __count ;
927+ const depthCol = cols . __depth ;
928+ const schemaKeys = Object . keys ( dataset . schema ) ;
929+ const schemaCols = schemaKeys . map (
930+ ( k ) => cols [ k ] as unknown as ArrayLike < SqlValue > ,
931+ ) ;
921932
922- for ( let i = 0 ; it . valid ( ) ; it . next ( ) , ++ i ) {
933+ for ( let i = 0 ; i < count ; i ++ ) {
923934 if ( i % 64 === 0 ) {
924935 if ( signal . isCancelled ) throw TASK_CANCELLED ;
925936 if ( task . shouldYield ( ) ) await task . yield ( ) ;
926937 }
927938
928- const id = it . __id ;
929- const ts = it . __ts ;
930- const count = it . __count ;
931- const depth = it . __depth ;
932- const title = this . getTitle ( it ) ;
933- const subtitle = this . getSubtitle ( it ) ;
934- const colorScheme = this . getColor ( it , title ) ;
935- const row = this . extractKeys ( it , dataset . schema ) ;
936-
937- xs [ i ] = ts ;
938- depths [ i ] = depth ;
939+ const row = this . buildSchemaRow ( schemaKeys , schemaCols , i ) ;
940+ const title = this . getTitle ( row ) ;
941+ const subtitle = this . getSubtitle ( row ) ;
942+ const colorScheme = this . getColor ( row , title ) ;
943+
944+ xs [ i ] = tsCol [ i ] ;
945+ depths [ i ] = depthCol [ i ] ;
939946 instants [ i ] = {
940- id,
947+ id : idCol [ i ] ,
941948 title,
942949 subtitle,
943950 colorScheme,
944- count,
951+ count : countCol [ i ] ,
945952 row,
946953 } ;
947954 }
@@ -1014,7 +1021,10 @@ export class SliceTrack<T extends RowSchema> implements TrackRenderer {
10141021 const patterns = new Uint8Array ( count ) ;
10151022 const slices = new Array < Slice < T > > ( count ) ;
10161023
1017- const it = sliceQueryRes . iter ( {
1024+ // Bulk-decode all columns in one pass. Fixed columns become typed arrays;
1025+ // schema columns are read per-row to build the `row` object the callbacks
1026+ // need. __end is nullable (NUM_NULL) so comes back as a plain array.
1027+ const cols = sliceQueryRes . decodeColumns ( {
10181028 __id : NUM ,
10191029 __start : NUM ,
10201030 __end : NUM_NULL ,
@@ -1023,38 +1033,42 @@ export class SliceTrack<T extends RowSchema> implements TrackRenderer {
10231033 __incomplete : NUM ,
10241034 ...dataset . schema ,
10251035 } ) ;
1036+ const idCol = cols . __id ;
1037+ const startCol = cols . __start ;
1038+ const endCol = cols . __end ;
1039+ const countCol = cols . __count ;
1040+ const depthCol = cols . __depth ;
1041+ const incompleteCol = cols . __incomplete ;
1042+ const schemaKeys = Object . keys ( dataset . schema ) ;
1043+ const schemaCols = schemaKeys . map ( ( k ) => cols [ k ] ) ;
10261044
1027- for ( let i = 0 ; it . valid ( ) ; it . next ( ) , ++ i ) {
1045+ for ( let i = 0 ; i < count ; i ++ ) {
10281046 if ( i % 64 === 0 ) {
10291047 if ( signal . isCancelled ) throw TASK_CANCELLED ;
10301048 if ( task . shouldYield ( ) ) await task . yield ( ) ;
10311049 }
10321050
1033- const count = it . __count ;
1034- const id = it . __id ;
1035- const start = it . __start ;
1036- const end = it . __end ;
1037- const depth = it . __depth ;
1038- const title = this . getTitle ( it ) ;
1039- const subtitle = this . getSubtitle ( it ) ;
1040- const colorScheme = this . getColor ( it , title ) ;
1041- const isIncomplete = it . __incomplete === 1 ;
1042- const row = this . extractKeys ( it , dataset . schema ) ;
1043-
1044- starts [ i ] = start ;
1051+ const row = this . buildSchemaRow ( schemaKeys , schemaCols , i ) ;
1052+ const title = this . getTitle ( row ) ;
1053+ const subtitle = this . getSubtitle ( row ) ;
1054+ const colorScheme = this . getColor ( row , title ) ;
1055+ const end = endCol [ i ] ;
1056+ const isIncomplete = incompleteCol [ i ] === 1 ;
1057+
1058+ starts [ i ] = startCol [ i ] ;
10451059 // Incomplete slices are assigned a +Infinity end
10461060 ends [ i ] = end === null ? Number . POSITIVE_INFINITY : end ;
1047- depths [ i ] = depth ;
1061+ depths [ i ] = depthCol [ i ] ;
10481062 patterns [ i ] = isIncomplete
10491063 ? RECT_PATTERN_FADE_RIGHT
1050- : ( this . attrs . slicePattern ?.( it ) ?? 0 ) ;
1064+ : ( this . attrs . slicePattern ?.( row ) ?? 0 ) ;
10511065 slices [ i ] = {
1052- id,
1066+ id : idCol [ i ] ,
10531067 title,
10541068 subtitle,
10551069 colorScheme,
1056- count,
1057- fillRatio : this . attrs . fillRatio ?.( it ) ?? 1 ,
1070+ count : countCol [ i ] ,
1071+ fillRatio : this . attrs . fillRatio ?.( row ) ?? 1 ,
10581072 row,
10591073 } ;
10601074 }
@@ -1069,16 +1083,23 @@ export class SliceTrack<T extends RowSchema> implements TrackRenderer {
10691083 } ;
10701084 }
10711085
1072- // Efficiently copy a sebset of keys from a raw value based on some template.
1073- // Note: Only the template's keys are used, the values are ignored (hence the
1074- // unknown value types).
1075- private extractKeys ( from : T , template : Record < keyof T , unknown > ) : T {
1076- const result = { } as T ;
1077- // eslint-disable-next-line guard-for-in
1078- for ( const k in template ) {
1079- result [ k ] = from [ k ] ;
1086+ // Builds a per-row object holding just the dataset.schema columns, read out
1087+ // of the columnar arrays returned by QueryResult.decodeColumns(). |keys| and
1088+ // |cols| are parallel arrays (key[k] is stored in cols[k]) precomputed once
1089+ // per query. This is the columnar replacement for the old per-row
1090+ // extractKeys(it, schema) copy: the resulting object is both stored as the
1091+ // slice/instant `row` and handed to the user-provided title/colorizer/pattern
1092+ // callbacks (which only ever read schema fields).
1093+ private buildSchemaRow (
1094+ keys : ReadonlyArray < string > ,
1095+ cols : ReadonlyArray < ArrayLike < SqlValue > > ,
1096+ i : number ,
1097+ ) : T {
1098+ const row : Record < string , SqlValue > = { } ;
1099+ for ( let k = 0 ; k < keys . length ; k ++ ) {
1100+ row [ keys [ k ] ] = cols [ k ] [ i ] ;
10801101 }
1081- return result ;
1102+ return row as T ;
10821103 }
10831104
10841105 private async deferChunkedTask ( ) {
0 commit comments