Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
teh-cmc committed Mar 30, 2023
1 parent 82f5d69 commit 43d60b3
Show file tree
Hide file tree
Showing 29 changed files with 2,346 additions and 3,327 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/re_arrow_store/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ arrow2 = { workspace = true, features = [
"compute_concatenate",
"compute_aggregate",
] }
arrow2_convert.workspace = true
document-features = "0.2"
indent = "0.1"
itertools = { workspace = true }
nohash-hasher = "0.2"
parking_lot.workspace = true
smallvec = { version = "1.0", features = ["const_generics"]}
static_assertions = "1.1"
thiserror.workspace = true

Expand Down
53 changes: 22 additions & 31 deletions crates/re_arrow_store/benches/data_store.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

use arrow2::array::{Array, UnionArray};
use arrow2::array::UnionArray;
use criterion::{criterion_group, criterion_main, Criterion};

use re_arrow_store::{DataStore, DataStoreConfig, LatestAtQuery, RangeQuery, TimeInt, TimeRange};
use re_log_types::{
component_types::{InstanceKey, Rect2D},
datagen::{build_frame_nr, build_some_instances, build_some_rects},
Component as _, ComponentName, DataRow, DataTable, EntityPath, MsgId, TimeType, Timeline,
Component as _, ComponentName, DataCell, DataRow, DataTable, EntityPath, MsgId, TimeType,
Timeline,
};

criterion_group!(benches, insert, latest_at, latest_at_missing, range);
Expand Down Expand Up @@ -52,10 +53,7 @@ fn insert(c: &mut Criterion) {
b.iter(|| {
insert_table(
DataStoreConfig {
index_bucket_nb_rows: num_rows_per_bucket,
component_bucket_nb_rows: num_rows_per_bucket,
index_bucket_size_bytes: u64::MAX,
component_bucket_size_bytes: u64::MAX,
indexed_bucket_num_rows: num_rows_per_bucket,
..Default::default()
},
InstanceKey::name(),
Expand All @@ -80,10 +78,11 @@ fn latest_at(c: &mut Criterion) {
group.bench_function("default", |b| {
let store = insert_table(Default::default(), InstanceKey::name(), &table);
b.iter(|| {
let results = latest_data_at(&store, Rect2D::name(), &[Rect2D::name()]);
let rects = results[0]
let cells = latest_data_at(&store, Rect2D::name(), &[Rect2D::name()]);
let rects = cells[0]
.as_ref()
.unwrap()
.as_arrow_ref()
.as_any()
.downcast_ref::<UnionArray>()
.unwrap();
Expand All @@ -96,21 +95,19 @@ fn latest_at(c: &mut Criterion) {
for num_rows_per_bucket in num_rows_per_bucket {
let store = insert_table(
DataStoreConfig {
index_bucket_nb_rows: num_rows_per_bucket,
component_bucket_nb_rows: num_rows_per_bucket,
index_bucket_size_bytes: u64::MAX,
component_bucket_size_bytes: u64::MAX,
indexed_bucket_num_rows: num_rows_per_bucket,
..Default::default()
},
InstanceKey::name(),
&table,
);
group.bench_function(format!("bucketsz={num_rows_per_bucket}"), |b| {
b.iter(|| {
let results = latest_data_at(&store, Rect2D::name(), &[Rect2D::name()]);
let rects = results[0]
let cells = latest_data_at(&store, Rect2D::name(), &[Rect2D::name()]);
let rects = cells[0]
.as_ref()
.unwrap()
.as_arrow_ref()
.as_any()
.downcast_ref::<UnionArray>()
.unwrap();
Expand Down Expand Up @@ -161,10 +158,7 @@ fn latest_at_missing(c: &mut Criterion) {
for num_rows_per_bucket in num_rows_per_bucket {
let store = insert_table(
DataStoreConfig {
index_bucket_nb_rows: num_rows_per_bucket,
component_bucket_nb_rows: num_rows_per_bucket,
index_bucket_size_bytes: u64::MAX,
component_bucket_size_bytes: u64::MAX,
indexed_bucket_num_rows: num_rows_per_bucket,
..Default::default()
},
InstanceKey::name(),
Expand Down Expand Up @@ -218,25 +212,23 @@ fn range(c: &mut Criterion) {
for num_rows_per_bucket in num_rows_per_bucket {
let store = insert_table(
DataStoreConfig {
index_bucket_nb_rows: num_rows_per_bucket,
component_bucket_nb_rows: num_rows_per_bucket,
index_bucket_size_bytes: u64::MAX,
component_bucket_size_bytes: u64::MAX,
indexed_bucket_num_rows: num_rows_per_bucket,
..Default::default()
},
InstanceKey::name(),
&table,
);
group.bench_function(format!("bucketsz={num_rows_per_bucket}"), |b| {
b.iter(|| {
let msgs = range_data(&store, [Rect2D::name()]);
for (cur_time, (time, results)) in msgs.enumerate() {
let rows = range_data(&store, [Rect2D::name()]);
for (cur_time, (time, cells)) in rows.enumerate() {
let time = time.unwrap();
assert_eq!(cur_time as i64, time.as_i64());

let rects = results[0]
let rects = cells[0]
.as_ref()
.unwrap()
.as_arrow_ref()
.as_any()
.downcast_ref::<UnionArray>()
.unwrap();
Expand Down Expand Up @@ -287,26 +279,25 @@ fn latest_data_at<const N: usize>(
store: &DataStore,
primary: ComponentName,
secondaries: &[ComponentName; N],
) -> [Option<Box<dyn Array>>; N] {
) -> [Option<DataCell>; N] {
let timeline_frame_nr = Timeline::new("frame_nr", TimeType::Sequence);
let timeline_query = LatestAtQuery::new(timeline_frame_nr, (NUM_ROWS / 2).into());
let ent_path = EntityPath::from("rects");

let row_indices = store
store
.latest_at(&timeline_query, &ent_path, primary, secondaries)
.unwrap_or_else(|| [(); N].map(|_| None));
store.get(secondaries, &row_indices)
.unwrap_or_else(|| [(); N].map(|_| None))
}

fn range_data<const N: usize>(
store: &DataStore,
components: [ComponentName; N],
) -> impl Iterator<Item = (Option<TimeInt>, [Option<Box<dyn Array>>; N])> + '_ {
) -> impl Iterator<Item = (Option<TimeInt>, [Option<DataCell>; N])> + '_ {
let timeline_frame_nr = Timeline::new("frame_nr", TimeType::Sequence);
let query = RangeQuery::new(timeline_frame_nr, TimeRange::new(0.into(), NUM_ROWS.into()));
let ent_path = EntityPath::from("rects");

store
.range(&query, &ent_path, components)
.map(move |(time, _, row_indices)| (time, store.get(&components, &row_indices)))
.map(move |(time, _, cells)| (time, cells))
}
8 changes: 3 additions & 5 deletions crates/re_arrow_store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
mod arrow_util;
mod store;
mod store_arrow;
mod store_format;
mod store_gc;
mod store_read;
Expand All @@ -33,17 +34,14 @@ pub mod polars_util;
pub mod test_util;

pub use self::arrow_util::ArrayExt;
pub use self::store::{
DataStore, DataStoreConfig, IndexBucket, IndexRowNr, IndexTable, RowIndex, RowIndexKind,
};
pub use self::store::{DataStore, DataStoreConfig};
pub use self::store_gc::GarbageCollectionTarget;
pub use self::store_read::{LatestAtQuery, RangeQuery};
pub use self::store_stats::DataStoreStats;
pub use self::store_write::{WriteError, WriteResult};

pub(crate) use self::store::{
ComponentBucket, ComponentTable, IndexBucketIndices, PersistentComponentTable,
PersistentIndexTable, SecondaryIndex, TimeIndex,
IndexedBucket, IndexedBucketInner, IndexedTable, PersistentIndexedTable,
};

// Re-exports
Expand Down
36 changes: 18 additions & 18 deletions crates/re_arrow_store/src/polars_util.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use arrow2::array::Array;
use itertools::Itertools;
use polars_core::{prelude::*, series::Series};
use polars_ops::prelude::*;
use re_log_types::{ComponentName, EntityPath, TimeInt};
use re_log_types::{ComponentName, DataCell, EntityPath, TimeInt};

use crate::{ArrayExt, DataStore, LatestAtQuery, RangeQuery};

Expand Down Expand Up @@ -38,12 +37,11 @@ pub fn latest_component(
let cluster_key = store.cluster_key();

let components = &[cluster_key, primary];
let row_indices = store
let cells = store
.latest_at(query, ent_path, primary, components)
.unwrap_or([None; 2]);
let results = store.get(components, &row_indices);
.unwrap_or([(); 2].map(|_| None));

dataframe_from_results(components, results)
dataframe_from_cells(&cells)
}

/// Queries any number of components and their cluster keys from their respective point-of-views,
Expand Down Expand Up @@ -161,12 +159,11 @@ pub fn range_components<'a, const N: usize>(
.chain(
store
.range(query, ent_path, components)
.map(move |(time, _, row_indices)| {
let results = store.get(&components, &row_indices);
.map(move |(time, _, cells)| {
(
time,
row_indices[primary_col].is_some(), // is_primary
dataframe_from_results(&components, results),
cells[primary_col].is_some(), // is_primary
dataframe_from_cells(&cells),
)
}),
)
Expand Down Expand Up @@ -200,16 +197,19 @@ pub fn range_components<'a, const N: usize>(

// --- Joins ---

pub fn dataframe_from_results<const N: usize>(
components: &[ComponentName; N],
results: [Option<Box<dyn Array>>; N],
// TODO: none of this mess should be here

pub fn dataframe_from_cells<const N: usize>(
cells: &[Option<DataCell>; N],
) -> SharedResult<DataFrame> {
let series: Result<Vec<_>, _> = components
let series: Result<Vec<_>, _> = cells
.iter()
.zip(results)
.filter_map(|(component, col)| col.map(|col| (component, col)))
.map(|(&component, col)| {
Series::try_from((component.as_str(), col.as_ref().clean_for_polars()))
.flatten()
.map(|cell| {
Series::try_from((
cell.component_name().as_str(),
cell.as_arrow_ref().clean_for_polars(),
))
})
.collect();

Expand Down
Loading

2 comments on commit 43d60b3

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust Benchmark

Benchmark suite Current: 43d60b3 Previous: 82f5d69 Ratio
datastore/num_rows=1000/num_instances=1000/packed=false/insert/default 8965930 ns/iter (± 622553) 10706570 ns/iter (± 389113) 0.84
datastore/num_rows=1000/num_instances=1000/packed=false/insert/bucketsz=0 16836015 ns/iter (± 789257) 12452965 ns/iter (± 359625) 1.35
datastore/num_rows=1000/num_instances=1000/packed=false/insert/bucketsz=2 21592517 ns/iter (± 1228569) 11718709 ns/iter (± 366919) 1.84
datastore/num_rows=1000/num_instances=1000/packed=false/insert/bucketsz=32 16492855 ns/iter (± 955390) 10344431 ns/iter (± 349602) 1.59
datastore/num_rows=1000/num_instances=1000/packed=false/insert/bucketsz=2048 9309968 ns/iter (± 567773) 10205868 ns/iter (± 334515) 0.91
datastore/num_rows=1000/num_instances=1000/packed=true/insert/default 8732011 ns/iter (± 526937) 9931804 ns/iter (± 351812) 0.88
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=0 17917953 ns/iter (± 1173455) 11597255 ns/iter (± 597620) 1.55
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=2 20984063 ns/iter (± 1027253) 11200708 ns/iter (± 457977) 1.87
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=32 16119467 ns/iter (± 751361) 10043978 ns/iter (± 350760) 1.60
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=2048 8762673 ns/iter (± 518622) 9999497 ns/iter (± 315662) 0.88
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/default 1942 ns/iter (± 1076) 1806 ns/iter (± 37) 1.08
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/bucketsz=0 1828 ns/iter (± 9) 1837 ns/iter (± 26) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/bucketsz=2 1813 ns/iter (± 10) 1829 ns/iter (± 25) 0.99
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/bucketsz=32 1816 ns/iter (± 7) 1804 ns/iter (± 25) 1.01
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/bucketsz=2048 1791 ns/iter (± 8) 1797 ns/iter (± 26) 1.00
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at/default 1987 ns/iter (± 1281) 1827 ns/iter (± 29) 1.09
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at/bucketsz=0 1775 ns/iter (± 12) 1838 ns/iter (± 24) 0.97
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at/bucketsz=2 1797 ns/iter (± 9) 1815 ns/iter (± 24) 0.99
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at/bucketsz=32 1811 ns/iter (± 9) 1807 ns/iter (± 22) 1.00
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at/bucketsz=2048 1763 ns/iter (± 8) 1814 ns/iter (± 27) 0.97
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/default 261 ns/iter (± 0) 273 ns/iter (± 4) 0.96
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/default 422 ns/iter (± 0) 420 ns/iter (± 5) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/bucketsz=0 262 ns/iter (± 0) 272 ns/iter (± 3) 0.96
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/bucketsz=0 429 ns/iter (± 0) 429 ns/iter (± 6) 1
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/bucketsz=2 262 ns/iter (± 0) 274 ns/iter (± 3) 0.96
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/bucketsz=2 429 ns/iter (± 0) 430 ns/iter (± 6) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/bucketsz=32 262 ns/iter (± 0) 272 ns/iter (± 4) 0.96
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/bucketsz=32 427 ns/iter (± 0) 426 ns/iter (± 6) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/bucketsz=2048 262 ns/iter (± 0) 273 ns/iter (± 4) 0.96
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/bucketsz=2048 422 ns/iter (± 0) 428 ns/iter (± 6) 0.99
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/primary/default 262 ns/iter (± 0) 272 ns/iter (± 4) 0.96
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/secondaries/default 424 ns/iter (± 0) 425 ns/iter (± 6) 1.00
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/primary/bucketsz=0 265 ns/iter (± 0) 275 ns/iter (± 4) 0.96
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/secondaries/bucketsz=0 439 ns/iter (± 0) 430 ns/iter (± 6) 1.02
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/primary/bucketsz=2 265 ns/iter (± 0) 271 ns/iter (± 3) 0.98
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/secondaries/bucketsz=2 433 ns/iter (± 0) 428 ns/iter (± 5) 1.01
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/primary/bucketsz=32 262 ns/iter (± 0) 272 ns/iter (± 3) 0.96
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/secondaries/bucketsz=32 430 ns/iter (± 0) 422 ns/iter (± 5) 1.02
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/primary/bucketsz=2048 261 ns/iter (± 0) 273 ns/iter (± 4) 0.96
datastore/num_rows=1000/num_instances=1000/packed=true/latest_at_missing/secondaries/bucketsz=2048 422 ns/iter (± 0) 420 ns/iter (± 6) 1.00
datastore/num_rows=1000/num_instances=1000/packed=false/range/default 9201069 ns/iter (± 746768) 10731768 ns/iter (± 391536) 0.86
datastore/num_rows=1000/num_instances=1000/packed=false/range/bucketsz=0 2491006 ns/iter (± 36696) 2426758 ns/iter (± 24070) 1.03
datastore/num_rows=1000/num_instances=1000/packed=false/range/bucketsz=2 2567463 ns/iter (± 16780) 2356495 ns/iter (± 23638) 1.09
datastore/num_rows=1000/num_instances=1000/packed=false/range/bucketsz=32 2281356 ns/iter (± 8010) 2056590 ns/iter (± 21943) 1.11
datastore/num_rows=1000/num_instances=1000/packed=false/range/bucketsz=2048 2918890 ns/iter (± 93762) 1939902 ns/iter (± 24654) 1.50
datastore/num_rows=1000/num_instances=1000/packed=true/range/default 8587936 ns/iter (± 546497) 10241736 ns/iter (± 538329) 0.84
datastore/num_rows=1000/num_instances=1000/packed=true/range/bucketsz=0 2462710 ns/iter (± 17983) 2375451 ns/iter (± 29926) 1.04
datastore/num_rows=1000/num_instances=1000/packed=true/range/bucketsz=2 2434200 ns/iter (± 9814) 2389685 ns/iter (± 22242) 1.02
datastore/num_rows=1000/num_instances=1000/packed=true/range/bucketsz=32 2167324 ns/iter (± 8679) 2044606 ns/iter (± 23931) 1.06
datastore/num_rows=1000/num_instances=1000/packed=true/range/bucketsz=2048 2745303 ns/iter (± 41171) 1982228 ns/iter (± 22101) 1.38
mono_points_arrow/generate_message_bundles 46044711 ns/iter (± 1008930) 40599288 ns/iter (± 697218) 1.13
mono_points_arrow/generate_messages 181245407 ns/iter (± 1416560) 165205809 ns/iter (± 1451326) 1.10
mono_points_arrow/encode_log_msg 225611576 ns/iter (± 1233026) 205478067 ns/iter (± 1474392) 1.10
mono_points_arrow/encode_total 449328850 ns/iter (± 2181088) 412623098 ns/iter (± 2059405) 1.09
mono_points_arrow/decode_log_msg 271556743 ns/iter (± 1401416) 253241563 ns/iter (± 1595261) 1.07
mono_points_arrow/decode_message_bundles 99741655 ns/iter (± 1253488) 84861512 ns/iter (± 1103824) 1.18
mono_points_arrow/decode_total 366693041 ns/iter (± 1818709) 336380010 ns/iter (± 2357202) 1.09
mono_points_arrow_batched/generate_message_bundles 36266685 ns/iter (± 2274194) 30957516 ns/iter (± 1271417) 1.17
mono_points_arrow_batched/generate_messages 10816828 ns/iter (± 1371701) 9263854 ns/iter (± 397210) 1.17
mono_points_arrow_batched/encode_log_msg 1826204 ns/iter (± 6712) 1931279 ns/iter (± 19131) 0.95
mono_points_arrow_batched/encode_total 50294058 ns/iter (± 3330999) 44449171 ns/iter (± 1420466) 1.13
mono_points_arrow_batched/decode_log_msg 971263 ns/iter (± 3654) 1065718 ns/iter (± 9497) 0.91
mono_points_arrow_batched/decode_message_bundles 17738995 ns/iter (± 1667775) 16651452 ns/iter (± 712473) 1.07
mono_points_arrow_batched/decode_total 19773829 ns/iter (± 1562094) 18118866 ns/iter (± 686053) 1.09
batch_points_arrow/generate_message_bundles 323509 ns/iter (± 1281) 280045 ns/iter (± 4128) 1.16
batch_points_arrow/generate_messages 7655 ns/iter (± 68) 7381 ns/iter (± 99) 1.04
batch_points_arrow/encode_log_msg 395111 ns/iter (± 2908) 425198 ns/iter (± 3168) 0.93
batch_points_arrow/encode_total 739840 ns/iter (± 4872) 717571 ns/iter (± 8166) 1.03
batch_points_arrow/decode_log_msg 340810 ns/iter (± 1260) 361435 ns/iter (± 3655) 0.94
batch_points_arrow/decode_message_bundles 2915 ns/iter (± 9) 2790 ns/iter (± 38) 1.04
batch_points_arrow/decode_total 351340 ns/iter (± 2756) 368416 ns/iter (± 3757) 0.95
arrow_mono_points/insert 6153622811 ns/iter (± 28848670) 6182615719 ns/iter (± 22343798) 1.00
arrow_mono_points/query 1861619 ns/iter (± 248576) 1851832 ns/iter (± 20835) 1.01
arrow_batch_points/insert 1728579 ns/iter (± 10192) 3113566 ns/iter (± 30277) 0.56
arrow_batch_points/query 16106 ns/iter (± 22) 16716 ns/iter (± 252) 0.96
arrow_batch_vecs/insert 32327 ns/iter (± 69) 45410 ns/iter (± 646) 0.71
arrow_batch_vecs/query 505527 ns/iter (± 372) 506513 ns/iter (± 6390) 1.00
tuid/Tuid::random 34 ns/iter (± 0) 33 ns/iter (± 0) 1.03

This comment was automatically generated by workflow using github-action-benchmark.

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rust Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: 43d60b3 Previous: 82f5d69 Ratio
datastore/num_rows=1000/num_instances=1000/packed=false/insert/bucketsz=2 21592517 ns/iter (± 1228569) 11718709 ns/iter (± 366919) 1.84
datastore/num_rows=1000/num_instances=1000/packed=false/insert/bucketsz=32 16492855 ns/iter (± 955390) 10344431 ns/iter (± 349602) 1.59
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=0 17917953 ns/iter (± 1173455) 11597255 ns/iter (± 597620) 1.55
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=2 20984063 ns/iter (± 1027253) 11200708 ns/iter (± 457977) 1.87
datastore/num_rows=1000/num_instances=1000/packed=true/insert/bucketsz=32 16119467 ns/iter (± 751361) 10043978 ns/iter (± 350760) 1.60
datastore/num_rows=1000/num_instances=1000/packed=false/range/bucketsz=2048 2918890 ns/iter (± 93762) 1939902 ns/iter (± 24654) 1.50

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.