diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1f72d589f..bda8111213 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1242,6 +1242,15 @@ importers: perspective-3-8-0: specifier: npm:@finos/perspective@3.8.0 version: '@finos/perspective@3.8.0' + perspective-4-0-0: + specifier: npm:@perspective-dev/client@4.0.0 + version: '@perspective-dev/client@4.0.0' + perspective-4-1-0: + specifier: npm:@perspective-dev/client@4.1.0 + version: '@perspective-dev/client@4.1.0' + perspective-4-2-0: + specifier: npm:@perspective-dev/client@4.2.0 + version: '@perspective-dev/client@4.2.0' devDependencies: '@perspective-dev/client': specifier: 'workspace:' @@ -3334,6 +3343,18 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@perspective-dev/client@4.0.0': + resolution: {integrity: sha512-n3IHMIU+WeraAxAcyBMG/Wipegm8PBcDL3+haZecriBvV4v+7EzNvZd8l6pQNbYa+ah5eD2BCgmvZ8Mg/dxUug==} + + '@perspective-dev/client@4.1.0': + resolution: {integrity: sha512-23VTzOmmBo1KynLTygFuM/RuOszSgIbvG68I3MNCS/w5BOoG4XGt+4WuZWTr13c93b+n3dGK18E4YF7hy82nwg==} + + '@perspective-dev/client@4.2.0': + resolution: {integrity: sha512-Gb9RFOoKnGAJDR0rZA9OBC8CY3Sv3zsEJfrmUhmnFWC21erk66DhppFXKYm3ggU2ob/yhCNA/yWMUv4n8OBqVQ==} + + '@perspective-dev/server@4.3.0': + resolution: {integrity: sha512-UH3ADscynozVx42RF07DTBmPE/0PUwH+SS0cgvMLuLfBjtvnPm6msfOU0tHlgFnNZOHJO0q6RZ9WI5fD6wF07A==} + '@playwright/experimental-ct-core@1.58.0': resolution: {integrity: sha512-YZsjApZmRE78Kp2E6OtAvFFVheUyZDfrlZMf+lfnSshmYHrrJUy3bhdCe7EPCWsE12XfCVVAv6G0btiyAx8d0w==} engines: {node: '>=18'} @@ -12473,6 +12494,44 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@perspective-dev/client@4.0.0': + dependencies: + '@perspective-dev/server': 4.3.0 + pro_self_extracting_wasm: 0.0.9 + stoppable: 1.1.0 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@perspective-dev/client@4.1.0': + dependencies: + '@perspective-dev/server': 4.3.0 + pro_self_extracting_wasm: 0.0.9 + stoppable: 1.1.0 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@perspective-dev/client@4.2.0': + dependencies: + '@perspective-dev/server': 4.3.0 + pro_self_extracting_wasm: 0.0.9 + stoppable: 1.1.0 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + '@perspective-dev/server@4.3.0': {} + '@playwright/experimental-ct-core@1.58.0(@types/node@24.9.1)(jiti@1.21.7)(less@4.4.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)': dependencies: playwright: 1.58.0 diff --git a/rust/perspective-server/cpp/perspective/CMakeLists.txt b/rust/perspective-server/cpp/perspective/CMakeLists.txt index 4e9fa6510e..1d8dd5c8b6 100644 --- a/rust/perspective-server/cpp/perspective/CMakeLists.txt +++ b/rust/perspective-server/cpp/perspective/CMakeLists.txt @@ -250,9 +250,16 @@ if(PSP_WASM_BUILD) ") endif() else() - set(OPT_FLAGS " -O3 -g${DEBUG_LEVEL} ") + set(OPT_FLAGS " \ + -O3 -g${DEBUG_LEVEL} \ + -mbulk-memory \ + -msimd128 \ + -mrelaxed-simd \ + -flto \ + --emit-tsd=perspective-server.d.ts \ + ") if (PSP_WASM_EXCEPTIONS) - set(OPT_FLAGS "${OPT_FLAGS} -fwasm-exceptions -flto --emit-tsd=perspective-server.d.ts ") + set(OPT_FLAGS "${OPT_FLAGS} -fwasm-exceptions ") endif() endif() elseif(PSP_CPP_BUILD OR PSP_PYTHON_BUILD) @@ -295,6 +302,7 @@ elseif(PSP_CPP_BUILD OR PSP_PYTHON_BUILD) -O3 \ -fexceptions \ -g1 \ + -fopenmp-simd \ ") if (PSP_WASM_EXCEPTIONS) set(OPT_FLAGS "${OPT_FLAGS} -fwasm-exceptions ") @@ -335,15 +343,9 @@ endif() if (PSP_WASM64) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ - -mbulk-memory \ - -msimd128 \ - -mrelaxed-simd \ -s MEMORY64=1 \ ") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ - -mbulk-memory \ - -msimd128 \ - -mrelaxed-simd \ -s MEMORY64=1 \ ") endif() diff --git a/rust/perspective-server/cpp/perspective/src/cpp/aggregate.cpp b/rust/perspective-server/cpp/perspective/src/cpp/aggregate.cpp index 08547a6bff..51f57f73ed 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/aggregate.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/aggregate.cpp @@ -25,8 +25,8 @@ t_aggregate::t_aggregate( ) : m_tree(tree), m_aggtype(aggtype), - m_icolumns(std::move(std::move(icolumns))), - m_ocolumn(std::move(std::move(ocolumn))) {} + m_icolumns(std::move(icolumns)), + m_ocolumn(std::move(ocolumn)) {} void t_aggregate::init() { diff --git a/rust/perspective-server/cpp/perspective/src/cpp/arg_sort.cpp b/rust/perspective-server/cpp/perspective/src/cpp/arg_sort.cpp index b4380ce8b9..2a13137b85 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/arg_sort.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/arg_sort.cpp @@ -38,44 +38,76 @@ t_argsort_comparator::t_argsort_comparator( bool t_argsort_comparator::operator()(t_index a, t_index b) const { - const t_tscalar& first = m_v[a]; - const t_tscalar& second = m_v[b]; - switch (m_sort_type) { - case SORTTYPE_ASCENDING: { - return (first < second); - } break; - case SORTTYPE_DESCENDING: { - return (first > second); - } break; - case SORTTYPE_ASCENDING_ABS: { - return std::abs(first.to_double()) < std::abs(second.to_double()); - } break; - case SORTTYPE_DESCENDING_ABS: { - - return std::abs(first.to_double()) > std::abs(second.to_double()); - } break; - case SORTTYPE_NONE: { - return a < b; - } + case SORTTYPE_ASCENDING: + return t_argsort_comparator_impl(m_v)(a, b); + case SORTTYPE_DESCENDING: + return t_argsort_comparator_impl(m_v)(a, b); + case SORTTYPE_ASCENDING_ABS: + return t_argsort_comparator_impl(m_v)(a, b); + case SORTTYPE_DESCENDING_ABS: + return t_argsort_comparator_impl(m_v)( + a, b + ); + case SORTTYPE_NONE: + return t_argsort_comparator_impl(m_v)(a, b); } return a < b; } +namespace { + +void +init_output(std::vector& output) { + for (t_index i = 0, loop_end = output.size(); i != loop_end; ++i) { + output[i] = i; + } +} + +} // anonymous namespace + void simple_argsort( std::vector& v, std::vector& output, const t_sorttype& sort_type ) { - // Output should be the same size is v - for (t_index i = 0, loop_end = output.size(); i != loop_end; ++i) { - output[i] = i; - } - t_argsort_comparator cmp(v, sort_type); + init_output(output); - std::sort(output.begin(), output.end(), cmp); + switch (sort_type) { + case SORTTYPE_ASCENDING: { + std::sort( + output.begin(), + output.end(), + t_argsort_comparator_impl(v) + ); + } break; + case SORTTYPE_DESCENDING: { + std::sort( + output.begin(), + output.end(), + t_argsort_comparator_impl(v) + ); + } break; + case SORTTYPE_ASCENDING_ABS: { + std::sort( + output.begin(), + output.end(), + t_argsort_comparator_impl(v) + ); + } break; + case SORTTYPE_DESCENDING_ABS: { + std::sort( + output.begin(), + output.end(), + t_argsort_comparator_impl(v) + ); + } break; + case SORTTYPE_NONE: { + // output is already identity-initialized + } break; + } } } // namespace perspective diff --git a/rust/perspective-server/cpp/perspective/src/cpp/base.cpp b/rust/perspective-server/cpp/perspective/src/cpp/base.cpp index dbd773430d..6ce0298749 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/base.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/base.cpp @@ -707,7 +707,7 @@ root_pidx() { bool is_internal_colname(const std::string& c) { - return c == std::string("psp_"); + return c == "psp_"; } template diff --git a/rust/perspective-server/cpp/perspective/src/cpp/context_grouped_pkey.cpp b/rust/perspective-server/cpp/perspective/src/cpp/context_grouped_pkey.cpp index fa648d7a75..07fe2d9198 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/context_grouped_pkey.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/context_grouped_pkey.cpp @@ -155,7 +155,7 @@ t_ctx_grouped_pkey::get_data( for (t_uindex aggidx = 0, loop_end = aggcols.size(); aggidx < loop_end; ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - aggcols[aggidx] = aggtable->get_const_column(aggname).get(); + aggcols[aggidx] = aggtable->_get_const_column(aggname); } const std::vector& aggspecs = m_config.get_aggregates(); @@ -520,12 +520,12 @@ t_ctx_grouped_pkey::rebuild() { }; const auto* sortby_col = - tbl->get_const_column(m_config.get_sort_by(child_col_name)).get(); + tbl->_get_const_column(m_config.get_sort_by(child_col_name)); const auto* parent_col = - tbl->get_const_column(m_config.get_parent_pkey_column()).get(); + tbl->_get_const_column(m_config.get_parent_pkey_column()); - const auto* pkey_col = tbl->get_const_column("psp_pkey").get(); + const auto* pkey_col = tbl->_get_const_column("psp_pkey"); std::vector data(nrows); tsl::hopscotch_map child_ridx_map; @@ -651,9 +651,9 @@ t_ctx_grouped_pkey::rebuild() { const t_aggspec& spec = aggspecs[aggnum]; if (spec.agg() == AGGTYPE_IDENTITY) { auto* scol = - aggtable->get_column(spec.get_first_depname()).get(); + aggtable->_get_column(spec.get_first_depname()); scol->copy( - tbl->get_const_column(spec.get_first_depname()).get(), + tbl->_get_const_column(spec.get_first_depname()), aggindices, 1 ); diff --git a/rust/perspective-server/cpp/perspective/src/cpp/context_one.cpp b/rust/perspective-server/cpp/perspective/src/cpp/context_one.cpp index 6f4e26fcb7..beebb28ac9 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/context_one.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/context_one.cpp @@ -120,7 +120,7 @@ t_ctx1::get_min_max(const std::string& colname) const { auto rval = std::make_pair(mknone(), mknone()); auto* aggtable = m_tree->get_aggtable(); t_schema aggschema = aggtable->get_schema(); - const auto* col = aggtable->get_const_column(colname).get(); + const auto* col = aggtable->_get_const_column(colname); auto colidx = aggschema.get_colidx(colname); auto depth = m_config.get_num_rpivots(); const std::vector& aggspecs = m_config.get_aggregates(); @@ -175,7 +175,6 @@ t_ctx1::get_data( t_index nrows = ext.m_erow - ext.m_srow; t_index stride = ext.m_ecol - ext.m_scol; - std::vector tmpvalues(nrows * ncols); std::vector values(nrows * stride); std::vector aggcols(m_config.get_num_aggregates()); @@ -187,7 +186,7 @@ t_ctx1::get_data( for (t_uindex aggidx = 0, loop_end = aggcols.size(); aggidx < loop_end; ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - aggcols[aggidx] = aggtable->get_const_column(aggname).get(); + aggcols[aggidx] = aggtable->_get_const_column(aggname); } const std::vector& aggspecs = m_config.get_aggregates(); @@ -200,28 +199,29 @@ t_ctx1::get_data( t_index agg_pridx = pnidx == INVALID_INDEX ? INVALID_INDEX : m_tree->get_aggidx(pnidx); - t_tscalar tree_value = m_tree->get_value(nidx); - tmpvalues[(ridx - ext.m_srow) * ncols] = tree_value; + t_index row_off = (ridx - ext.m_srow) * stride; + + if (ext.m_scol == 0) { + t_tscalar tree_value = m_tree->get_value(nidx); + values[row_off] = tree_value; + } for (t_index aggidx = 0, loop_end = aggcols.size(); aggidx < loop_end; ++aggidx) { + t_index col = 1 + aggidx; + if (col < ext.m_scol || col >= ext.m_ecol) { + continue; + } t_tscalar value = extract_aggregate( aggspecs[aggidx], aggcols[aggidx], agg_ridx, agg_pridx ); if (!value.is_valid()) { - value.set(none); // todo: fix null handling + value.set(none); } - tmpvalues[(ridx - ext.m_srow) * ncols + 1 + aggidx].set(value); + values[row_off + col - ext.m_scol].set(value); } } - for (auto ridx = ext.m_srow; ridx < ext.m_erow; ++ridx) { - for (auto cidx = ext.m_scol; cidx < ext.m_ecol; ++cidx) { - auto insert_idx = (ridx - ext.m_srow) * stride + cidx - ext.m_scol; - auto src_idx = (ridx - ext.m_srow) * ncols + cidx; - values[insert_idx].set(tmpvalues[src_idx]); - } - } return values; } @@ -232,7 +232,6 @@ t_ctx1::get_data(const std::vector& rows) const { t_uindex nrows = rows.size(); t_uindex ncols = get_column_count(); - std::vector tmpvalues(nrows * ncols); std::vector values(nrows * ncols); std::vector aggcols(m_config.get_num_aggregates()); @@ -244,13 +243,11 @@ t_ctx1::get_data(const std::vector& rows) const { for (t_uindex aggidx = 0, loop_end = aggcols.size(); aggidx < loop_end; ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - aggcols[aggidx] = aggtable->get_const_column(aggname).get(); + aggcols[aggidx] = aggtable->_get_const_column(aggname); } const std::vector& aggspecs = m_config.get_aggregates(); - // access data for changed rows, but write them into the slice as if we - // start from 0 for (t_uindex idx = 0; idx < nrows; ++idx) { t_uindex ridx = rows[idx]; t_index nidx = m_traversal->get_tree_index(ridx); @@ -261,7 +258,7 @@ t_ctx1::get_data(const std::vector& rows) const { pnidx == INVALID_INDEX ? INVALID_INDEX : m_tree->get_aggidx(pnidx); t_tscalar tree_value = m_tree->get_value(nidx); - tmpvalues[idx * ncols] = tree_value; + values[idx * ncols] = tree_value; for (t_index aggidx = 0, loop_end = aggcols.size(); aggidx < loop_end; ++aggidx) { @@ -269,16 +266,9 @@ t_ctx1::get_data(const std::vector& rows) const { aggspecs[aggidx], aggcols[aggidx], agg_ridx, agg_pridx ); if (!value.is_valid()) { - value.set(none); // todo: fix null handling + value.set(none); } - tmpvalues[idx * ncols + 1 + aggidx].set(value); - } - } - - for (t_uindex ridx = 0; ridx < nrows; ++ridx) { - for (t_uindex cidx = 0; cidx < ncols; ++cidx) { - t_uindex idx = ridx * ncols + cidx; - values[idx].set(tmpvalues[idx]); + values[idx * ncols + 1 + aggidx].set(value); } } @@ -632,7 +622,7 @@ t_ctx1::pprint() const { for (t_uindex aggidx = 0, loop_end = aggcols.size(); aggidx < loop_end; ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - aggcols[aggidx] = aggtable->get_const_column(aggname).get(); + aggcols[aggidx] = aggtable->_get_const_column(aggname); } const std::vector& aggspecs = m_config.get_aggregates(); diff --git a/rust/perspective-server/cpp/perspective/src/cpp/context_two.cpp b/rust/perspective-server/cpp/perspective/src/cpp/context_two.cpp index 28c8be68a3..83d5489773 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/context_two.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/context_two.cpp @@ -241,27 +241,17 @@ t_ctx2::get_min_max(const std::string& colname) const { auto rval = std::make_pair(mknone(), mknone()); t_uindex scol = m_trees[0]->get_aggtable()->get_schema().get_colidx(colname); - std::vector> cells; - for (t_index ridx = 0; ridx < ctx_nrows; ++ridx) { - for (t_index cidx = 0; cidx < ctx_ncols; ++cidx) { - cells.emplace_back(std::pair(ridx, cidx)); - } - } - - auto cells_info = resolve_cells(cells); - typedef std::pair t_aggpair; - std::map aggmap; + auto cells_info = resolve_cells(0, ctx_nrows, 0, ctx_ncols); auto n_aggs = m_config.get_num_aggregates(); - for (t_uindex treeidx = 0, tree_loop_end = m_trees.size(); - treeidx < tree_loop_end; - ++treeidx) { + t_uindex ntrees = m_trees.size(); + std::vector aggcols(ntrees * n_aggs); + for (t_uindex treeidx = 0; treeidx < ntrees; ++treeidx) { auto* aggtable = m_trees[treeidx]->get_aggtable(); t_schema aggschema = aggtable->get_schema(); - for (t_uindex aggidx = 0, agg_loop_end = n_aggs; aggidx < agg_loop_end; - ++aggidx) { + for (t_uindex aggidx = 0; aggidx < t_uindex(n_aggs); ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - aggmap[t_aggpair(treeidx, aggidx)] = - aggtable->get_const_column(aggname).get(); + aggcols[treeidx * n_aggs + aggidx] = + aggtable->_get_const_column(aggname); } } @@ -286,7 +276,7 @@ t_ctx2::get_min_max(const std::string& colname) const { } const auto* aggcol = - aggmap[t_aggpair(cinfo.m_treenum, cinfo.m_agg_index)]; + aggcols[cinfo.m_treenum * n_aggs + cinfo.m_agg_index]; t_index p_idx = m_trees[cinfo.m_treenum]->get_parent_idx(cinfo.m_idx); t_uindex agg_ridx = @@ -329,14 +319,7 @@ t_ctx2::get_data( ctx_nrows, ctx_ncols, start_row, end_row, start_col, end_col ); - std::vector> cells; - for (t_index ridx = ext.m_srow; ridx < ext.m_erow; ++ridx) { - for (t_index cidx = ext.m_scol; cidx < ext.m_ecol; ++cidx) { - cells.emplace_back(std::pair(ridx, cidx)); - } - } - - auto cells_info = resolve_cells(cells); + auto cells_info = resolve_cells(ext.m_srow, ext.m_erow, ext.m_scol, ext.m_ecol); t_index nrows = ext.m_erow - ext.m_srow; t_index stride = ext.m_ecol - ext.m_scol; @@ -344,22 +327,18 @@ t_ctx2::get_data( t_tscalar empty = mknone(); - typedef std::pair t_aggpair; - std::map aggmap; + t_uindex ntrees = m_trees.size(); + t_uindex naggs = m_config.get_num_aggregates(); + std::vector aggcols(ntrees * naggs); - for (t_uindex treeidx = 0, tree_loop_end = m_trees.size(); - treeidx < tree_loop_end; - ++treeidx) { + for (t_uindex treeidx = 0; treeidx < ntrees; ++treeidx) { auto* aggtable = m_trees[treeidx]->get_aggtable(); t_schema aggschema = aggtable->get_schema(); - for (t_uindex aggidx = 0, agg_loop_end = m_config.get_num_aggregates(); - aggidx < agg_loop_end; - ++aggidx) { + for (t_uindex aggidx = 0; aggidx < naggs; ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - - aggmap[t_aggpair(treeidx, aggidx)] = - aggtable->get_const_column(aggname).get(); + aggcols[treeidx * naggs + aggidx] = + aggtable->_get_const_column(aggname); } } @@ -382,7 +361,7 @@ t_ctx2::get_data( retval[insert_idx].set(empty); } else { const auto* aggcol = - aggmap[t_aggpair(cinfo.m_treenum, cinfo.m_agg_index)]; + aggcols[cinfo.m_treenum * naggs + cinfo.m_agg_index]; t_index p_idx = m_trees[cinfo.m_treenum]->get_parent_idx(cinfo.m_idx); @@ -449,31 +428,24 @@ t_ctx2::get_data(const std::vector& rows) const { t_tscalar empty = mknone(); - typedef std::pair t_aggpair; - std::map aggmap; + t_uindex ntrees = m_trees.size(); + t_uindex naggs = m_config.get_num_aggregates(); + std::vector aggcols(ntrees * naggs); - for (t_uindex treeidx = 0, tree_loop_end = m_trees.size(); - treeidx < tree_loop_end; - ++treeidx) { + for (t_uindex treeidx = 0; treeidx < ntrees; ++treeidx) { auto* aggtable = m_trees[treeidx]->get_aggtable(); t_schema aggschema = aggtable->get_schema(); - for (t_uindex aggidx = 0, agg_loop_end = m_config.get_num_aggregates(); - aggidx < agg_loop_end; - ++aggidx) { + for (t_uindex aggidx = 0; aggidx < naggs; ++aggidx) { const std::string& aggname = aggschema.m_columns[aggidx]; - - aggmap[t_aggpair(treeidx, aggidx)] = - aggtable->get_const_column(aggname).get(); + aggcols[treeidx * naggs + aggidx] = + aggtable->_get_const_column(aggname); } } const std::vector& aggspecs = m_config.get_aggregates(); - // // Write each row sequentially starting from 0 for (t_uindex ridx = 0; ridx < nrows; ++ridx) { - - // Write each column starting from 1 in order to skip __ROW_PATH__. for (t_uindex cidx = 1; cidx < ncols; ++cidx) { t_uindex insert_idx = ridx * ncols + cidx; const t_cellinfo& cinfo = cells_info[insert_idx]; @@ -482,7 +454,7 @@ t_ctx2::get_data(const std::vector& rows) const { rval[insert_idx].set(empty); } else { const auto* aggcol = - aggmap[t_aggpair(cinfo.m_treenum, cinfo.m_agg_index)]; + aggcols[cinfo.m_treenum * naggs + cinfo.m_agg_index]; t_index p_idx = m_trees[cinfo.m_treenum]->get_parent_idx(cinfo.m_idx); @@ -772,6 +744,102 @@ t_ctx2::resolve_cells(const std::vector>& cells return rval; } +std::vector +t_ctx2::resolve_cells( + t_uindex srow, t_uindex erow, t_uindex scol, t_uindex ecol +) const { + t_uindex nrows = erow - srow; + t_uindex ncols_range = ecol - scol; + std::vector rval(nrows * ncols_range); + + t_index n_aggs = m_config.get_num_aggregates(); + std::vector c_tvindices = get_ctraversal_indices(); + + std::vector> col_paths(m_ctraversal->size()); + + for (t_index cidx = 0, loop_end = c_tvindices.size(); cidx < loop_end; + ++cidx) { + auto translated = c_tvindices[cidx]; + const t_tvnode& c_tvnode = m_ctraversal->get_node(translated); + col_paths[cidx].reserve(m_config.get_num_cpivots()); + col_paths[cidx] = get_column_path(c_tvnode); + } + + t_uindex view_ncols = get_num_view_columns(); + + for (t_uindex ridx = srow; ridx < erow; ++ridx) { + bool row_valid = ridx < m_rtraversal->size(); + t_index r_ptidx = 0; + t_depth r_depth = 0; + std::vector r_path; + if (row_valid) { + const t_tvnode& r_tvnode = m_rtraversal->get_node(ridx); + r_ptidx = r_tvnode.m_tnid; + r_depth = r_tvnode.m_depth; + r_path = get_row_path(r_tvnode); + } + + for (t_uindex cidx = scol; cidx < ecol; ++cidx) { + t_uindex out_idx = (ridx - srow) * ncols_range + (cidx - scol); + + if (!row_valid || cidx == 0 || cidx >= view_ncols) { + rval[out_idx].m_idx = INVALID_INDEX; + continue; + } + + t_index agg_idx = (cidx - 1) % n_aggs; + t_uindex translated_cidx = calc_translated_colidx(n_aggs, cidx); + if (translated_cidx >= c_tvindices.size()) { + rval[out_idx].m_idx = INVALID_INDEX; + continue; + } + + t_index c_tvidx = c_tvindices[translated_cidx]; + + rval[out_idx].m_ridx = ridx; + rval[out_idx].m_cidx = cidx; + + if (c_tvidx >= t_index(m_ctraversal->size())) { + rval[out_idx].m_idx = INVALID_INDEX; + continue; + } + + const t_tvnode& c_tvnode = m_ctraversal->get_node(c_tvidx); + t_index c_ptidx = c_tvnode.m_tnid; + const std::vector& c_path = col_paths[translated_cidx]; + + rval[out_idx].m_agg_index = agg_idx; + + if (ridx == 0 && !m_leaves_only) { + rval[out_idx].m_idx = c_ptidx; + rval[out_idx].m_treenum = 0; + } else if (c_path.empty()) { + rval[out_idx].m_idx = r_ptidx; + rval[out_idx].m_treenum = m_trees.size() - 1; + } else { + t_index tree_idx = r_depth; + rval[out_idx].m_treenum = tree_idx; + + if (r_depth + 1 == static_cast(m_trees.size())) { + rval[out_idx].m_idx = + m_trees[tree_idx]->resolve_path(r_ptidx, c_path); + } else { + t_index path_ptidx = + m_trees[tree_idx]->resolve_path(0, r_path); + if (path_ptidx < 0) { + rval[out_idx].m_idx = INVALID_INDEX; + } else { + rval[out_idx].m_idx = + m_trees[tree_idx]->resolve_path(path_ptidx, c_path); + } + } + } + } + } + + return rval; +} + t_index t_ctx2::sidedness() const { return 2; diff --git a/rust/perspective-server/cpp/perspective/src/cpp/context_zero.cpp b/rust/perspective-server/cpp/perspective/src/cpp/context_zero.cpp index c038eece4d..bccf42da1b 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/context_zero.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/context_zero.cpp @@ -97,35 +97,30 @@ t_ctx0::notify( ) { t_uindex nrecs = flattened.size(); - std::shared_ptr pkey_sptr = - flattened.get_const_column("psp_pkey"); - std::shared_ptr op_sptr = - flattened.get_const_column("psp_op"); - const t_column* pkey_col = pkey_sptr.get(); - const t_column* op_col = op_sptr.get(); - - std::shared_ptr existed_sptr = - existed.get_const_column("psp_existed"); - const t_column* existed_col = existed_sptr.get(); + const t_column* pkey_col = flattened._get_const_column("psp_pkey"); + const t_column* op_col = flattened._get_const_column("psp_op"); + const t_column* existed_col = existed._get_const_column("psp_existed"); bool delete_encountered = false; + bool has_filters = m_config.has_filters(); + t_mask msk_prev, msk_curr; + if (has_filters) { + msk_prev = filter_table_for_config(prev, m_config); + msk_curr = filter_table_for_config(curr, m_config); + } - if (m_config.has_filters()) { - t_mask msk_prev = filter_table_for_config(prev, m_config); - t_mask msk_curr = filter_table_for_config(curr, m_config); - - for (t_uindex idx = 0; idx < nrecs; ++idx) { - t_tscalar pkey = - m_symtable.get_interned_tscalar(pkey_col->get_scalar(idx)); - - std::uint8_t op_ = *(op_col->get_nth(idx)); - t_op op = static_cast(op_); - bool existed = *(existed_col->get_nth(idx)); + for (t_uindex idx = 0; idx < nrecs; ++idx) { + t_tscalar pkey = + m_symtable.get_interned_tscalar(pkey_col->get_scalar(idx)); + std::uint8_t op_ = *(op_col->get_nth(idx)); + t_op op = static_cast(op_); + bool row_existed = *(existed_col->get_nth(idx)); - switch (op) { - case OP_INSERT: { + switch (op) { + case OP_INSERT: { + if (has_filters) { bool filter_curr = msk_curr.get(idx); - bool filter_prev = msk_prev.get(idx) && existed; + bool filter_prev = msk_prev.get(idx) && row_existed; if (filter_prev) { if (filter_curr) { @@ -148,49 +143,22 @@ t_ctx0::notify( ); } } - } break; - case OP_DELETE: { - m_traversal->delete_row(pkey); - delete_encountered = true; - } break; - default: { - PSP_COMPLAIN_AND_ABORT("Unexpected OP"); - } break; - } - - // add the pkey for row delta - add_delta_pkey(pkey); - } - m_has_delta = - !m_deltas->empty() || !m_delta_pkeys.empty() || delete_encountered; - - return; - } - - // Context does not have filters applied - for (t_uindex idx = 0; idx < nrecs; ++idx) { - t_tscalar pkey = - m_symtable.get_interned_tscalar(pkey_col->get_scalar(idx)); - std::uint8_t op_ = *(op_col->get_nth(idx)); - t_op op = static_cast(op_); - bool existed = *(existed_col->get_nth(idx)); - - switch (op) { - case OP_INSERT: { - if (existed) { - m_traversal->update_row( - *m_gstate, - *(m_expression_tables->m_master), - m_config, - pkey - ); } else { - m_traversal->add_row( - *m_gstate, - *(m_expression_tables->m_master), - m_config, - pkey - ); + if (row_existed) { + m_traversal->update_row( + *m_gstate, + *(m_expression_tables->m_master), + m_config, + pkey + ); + } else { + m_traversal->add_row( + *m_gstate, + *(m_expression_tables->m_master), + m_config, + pkey + ); + } } } break; case OP_DELETE: { @@ -202,7 +170,6 @@ t_ctx0::notify( } break; } - // add the pkey for row delta add_delta_pkey(pkey); } @@ -343,20 +310,35 @@ t_ctx0::get_data( m_traversal->get_pkeys(ext.m_srow, ext.m_erow); auto none = mknone(); + std::vector row_indices; + m_gstate->resolve_pkeys(pkeys, row_indices); + + std::shared_ptr master_table = m_gstate->get_table(); + t_index col_count = ext.m_ecol - ext.m_scol; + std::vector columns(col_count); for (t_index cidx = ext.m_scol; cidx < ext.m_ecol; ++cidx) { - std::vector out_data(pkeys.size()); const std::string& colname = m_config.col_at(cidx); - read_column_from_gstate(colname, pkeys, out_data); - - for (t_index ridx = ext.m_srow; ridx < ext.m_erow; ++ridx) { - auto v = out_data[ridx - ext.m_srow]; + if (is_expression_column(colname)) { + columns[cidx - ext.m_scol] = + m_expression_tables->m_master->_get_const_column(colname); + } else { + columns[cidx - ext.m_scol] = + master_table->_get_const_column(colname); + } + } - // todo: fix null handling + constexpr t_uindex INVALID_ROW = t_uindex(-1); + for (t_index ridx = 0; ridx < nrows; ++ridx) { + t_uindex row_idx = row_indices[ridx]; + if (row_idx == INVALID_ROW) { + continue; + } + for (t_index cidx = 0; cidx < col_count; ++cidx) { + auto v = columns[cidx]->get_scalar(row_idx); if (!v.is_valid()) { v.set(none); } - - values[(ridx - ext.m_srow) * stride + (cidx - ext.m_scol)] = v; + values[ridx * stride + cidx] = v; } } @@ -377,19 +359,34 @@ t_ctx0::get_data(const std::vector& rows) const { std::vector pkeys = m_traversal->get_pkeys(rows); auto none = mknone(); + + std::vector row_indices; + m_gstate->resolve_pkeys(pkeys, row_indices); + + std::shared_ptr master_table = m_gstate->get_table(); + std::vector columns(stride); for (t_uindex cidx = 0; cidx < stride; ++cidx) { - std::vector out_data(rows.size()); const std::string& colname = m_config.col_at(cidx); - read_column_from_gstate(colname, pkeys, out_data); - - for (t_uindex ridx = 0; ridx < rows.size(); ++ridx) { - auto v = out_data[ridx]; + if (is_expression_column(colname)) { + columns[cidx] = + m_expression_tables->m_master->_get_const_column(colname); + } else { + columns[cidx] = master_table->_get_const_column(colname); + } + } + constexpr t_uindex INVALID_ROW = t_uindex(-1); + for (t_uindex ridx = 0; ridx < rows.size(); ++ridx) { + t_uindex row_idx = row_indices[ridx]; + if (row_idx == INVALID_ROW) { + continue; + } + for (t_uindex cidx = 0; cidx < stride; ++cidx) { + auto v = columns[cidx]->get_scalar(row_idx); if (!v.is_valid()) { v.set(none); } - - values[(ridx)*stride + (cidx)] = v; + values[ridx * stride + cidx] = v; } } @@ -506,13 +503,13 @@ t_ctx0::calc_step_delta(const t_data_table& flattened) { // data for the first time, so every single row is a new delta. t_uindex nrows = flattened.size(); const auto& column_names = m_config.get_column_names(); - const t_column* pkey_col = flattened.get_const_column("psp_pkey").get(); + const t_column* pkey_col = flattened._get_const_column("psp_pkey"); // Add every row and every column to the delta for (const auto& name : column_names) { auto cidx = m_config.get_colidx(name); const t_column* flattened_column = - flattened.get_const_column(name).get(); + flattened._get_const_column(name); for (t_uindex ridx = 0; ridx < nrows; ++ridx) { m_deltas->insert(t_zcdelta( @@ -539,15 +536,15 @@ t_ctx0::calc_step_delta( PSP_VERBOSE_ASSERT(prev.size() == nrows, "Shape violation detected"); PSP_VERBOSE_ASSERT(curr.size() == nrows, "Shape violation detected"); - const t_column* pkey_col = flattened.get_const_column("psp_pkey").get(); + const t_column* pkey_col = flattened._get_const_column("psp_pkey"); const auto& column_names = m_config.get_column_names(); for (const auto& name : column_names) { auto cidx = m_config.get_colidx(name); - const t_column* tcol = transitions.get_const_column(name).get(); - const t_column* pcol = prev.get_const_column(name).get(); - const t_column* ccol = curr.get_const_column(name).get(); + const t_column* tcol = transitions._get_const_column(name); + const t_column* pcol = prev._get_const_column(name); + const t_column* ccol = curr._get_const_column(name); for (t_uindex ridx = 0; ridx < nrows; ++ridx) { const auto* trans_ = tcol->get_nth(ridx); diff --git a/rust/perspective-server/cpp/perspective/src/cpp/data_table.cpp b/rust/perspective-server/cpp/perspective/src/cpp/data_table.cpp index 272b2b91cf..a50c129fa5 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/data_table.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/data_table.cpp @@ -311,6 +311,32 @@ t_data_table::_get_column(std::string_view colname) { return m_columns[idx].get(); } +const t_column* +t_data_table::_get_const_column(std::string_view colname) const { + PSP_TRACE_SENTINEL(); + PSP_VERBOSE_ASSERT(m_init, "touching uninited object"); + t_uindex idx = m_schema.get_colidx(colname.data()); + return m_columns[idx].get(); +} + +const t_column* +t_data_table::_get_const_column(t_uindex idx) const { + PSP_TRACE_SENTINEL(); + PSP_VERBOSE_ASSERT(m_init, "touching uninited object"); + return m_columns[idx].get(); +} + +const t_column* +t_data_table::_get_const_column_safe(std::string_view colname) const { + PSP_TRACE_SENTINEL(); + PSP_VERBOSE_ASSERT(m_init, "touching uninited object"); + t_uindex idx = m_schema.get_colidx_safe(colname.data()); + if (idx == t_uindex(-1)) { + return nullptr; + } + return m_columns[idx].get(); +} + const t_schema& t_data_table::get_schema() const { PSP_TRACE_SENTINEL(); @@ -445,8 +471,8 @@ t_data_table::append(const t_data_table& other) { std::cout << ss.str(); PSP_COMPLAIN_AND_ABORT(ss.str()) } - src_cols.push_back(other.get_const_column(cname).get()); - dst_cols.push_back(get_column(cname).get()); + src_cols.push_back(other._get_const_column(cname)); + dst_cols.push_back(_get_column(cname)); incoming.insert(cname); } t_uindex other_size = other.num_rows(); @@ -489,7 +515,7 @@ t_data_table::filter_cpp( for (t_uindex idx = 0; idx < fterm_size; ++idx) { indices[idx] = m_schema.get_colidx(fterms[idx].m_colname); - columns[idx] = get_const_column(fterms[idx].m_colname).get(); + columns[idx] = _get_const_column(fterms[idx].m_colname); fterms[idx].coerce_numeric(columns[idx]->get_dtype()); if (fterms[idx].m_use_interned) { t_tscalar& thr = fterms[idx].m_threshold; @@ -866,6 +892,7 @@ t_data_table::get_scalvec() const { auto cols = get_const_columns(); auto ncols = cols.size(); std::vector rv; + rv.reserve(nrows * ncols); for (t_uindex idx = 0; idx < nrows; ++idx) { for (t_uindex cidx = 0; cidx < ncols; ++cidx) { rv.push_back(cols[cidx]->get_scalar(idx)); diff --git a/rust/perspective-server/cpp/perspective/src/cpp/dense_tree.cpp b/rust/perspective-server/cpp/perspective/src/cpp/dense_tree.cpp index 907900319d..cb88d0cd4e 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/dense_tree.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/dense_tree.cpp @@ -18,6 +18,7 @@ #include #include +#include #include namespace perspective { @@ -28,7 +29,7 @@ t_dtree::t_dtree( const std::vector>& sortby_colvec ) : m_levels_pivoted(0), - m_ds(std::move(std::move(ds))), + m_ds(std::move(ds)), m_pivots(pivots), m_nidx(0), m_backing_store(BACKING_STORE_MEMORY), @@ -55,7 +56,7 @@ t_dtree::t_dtree( ) : m_dirname(std::move(dirname)), m_levels_pivoted(0), - m_ds(std::move(std::move(ds))), + m_ds(std::move(ds)), m_pivots(pivots), m_nidx(0), m_backing_store(backing_store), @@ -185,10 +186,7 @@ t_dtree::pivot(const t_filter& filter, t_uindex level) { if (m_levels_pivoted == 0) { m_leaves.extend(nrows); auto* leaves = m_leaves.get_nth(0); - - for (t_uindex idx = 0; idx < nrows; idx++) { - leaves[idx] = idx; - } + std::iota(leaves, leaves + nrows, t_uindex(0)); nbidx = 0; neidx = 1; @@ -209,7 +207,7 @@ t_dtree::pivot(const t_filter& filter, t_uindex level) { } else { const t_pivot& pivot = m_pivots[pidx - 1]; std::string pivot_colname = pivot.colname(); - pivcol = m_ds->get_const_column(pivot_colname).get(); + pivcol = m_ds->_get_const_column(pivot_colname); t_dtype piv_dtype = pivcol->get_dtype(); t_uindex next_neidx = 0; diff --git a/rust/perspective-server/cpp/perspective/src/cpp/dense_tree_context.cpp b/rust/perspective-server/cpp/perspective/src/cpp/dense_tree_context.cpp index 27620f2592..4e7ea77988 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/dense_tree_context.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/dense_tree_context.cpp @@ -128,7 +128,7 @@ t_dtree_ctx::pprint(const t_filter& fltr) const { t_uindex ncols = 0; for (const std::string& colname : m_aggregates->get_schema().m_columns) { - aggcols.push_back(m_aggregates->get_const_column(colname).get()); + aggcols.push_back(m_aggregates->_get_const_column(colname)); std::cout << colname << ", "; ++ncols; } @@ -184,8 +184,8 @@ void t_dtree_ctx::pprint_strands() const { std::vector columns; const auto* scount_col = - m_strand_deltas->get_const_column("psp_strand_count").get(); - const auto* pkey_col = m_strands->get_const_column("psp_pkey").get(); + m_strand_deltas->_get_const_column("psp_strand_count"); + const auto* pkey_col = m_strands->_get_const_column("psp_pkey"); auto strand_schema = m_strands->get_schema(); t_uindex width = 18; @@ -193,7 +193,7 @@ t_dtree_ctx::pprint_strands() const { std::vector colnames = {"psp_pkey", "psp_strand_count"}; for (const auto& colname : strand_schema.m_columns) { - const auto* col = m_strands->get_const_column(colname).get(); + const auto* col = m_strands->_get_const_column(colname); if (col != pkey_col) { columns.push_back(col); colnames.push_back(colname); @@ -202,7 +202,7 @@ t_dtree_ctx::pprint_strands() const { auto strand_delta_schema = m_strand_deltas->get_schema(); for (const auto& colname : strand_delta_schema.m_columns) { - const auto* col = m_strand_deltas->get_const_column(colname).get(); + const auto* col = m_strand_deltas->_get_const_column(colname); if (col != scount_col) { columns.push_back(col); std::stringstream ss; @@ -246,17 +246,17 @@ t_dtree_ctx::pprint_strands_tree() const { std::vector columns; columns.emplace_back( - "psp_pkey", m_strands->get_const_column("psp_pkey").get() + "psp_pkey", m_strands->_get_const_column("psp_pkey") ); columns.emplace_back( "psp_strand_count", - m_strand_deltas->get_const_column("psp_strand_count").get() + m_strand_deltas->_get_const_column("psp_strand_count") ); for (const auto& piv : m_tree.get_pivots()) { columns.emplace_back( - piv.colname(), m_strands->get_const_column(piv.colname()).get() + piv.colname(), m_strands->_get_const_column(piv.colname()) ); } diff --git a/rust/perspective-server/cpp/perspective/src/cpp/gnode.cpp b/rust/perspective-server/cpp/perspective/src/cpp/gnode.cpp index ad9427aa5d..70640e9ed3 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/gnode.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/gnode.cpp @@ -215,11 +215,11 @@ t_gnode::_process_mask_existed_rows(t_process_state& process_state) { auto flattened_num_rows = process_state.m_flattened_data_table->num_rows(); process_state.m_existed_data_table->set_size(flattened_num_rows); - std::shared_ptr op_col = - process_state.m_flattened_data_table->get_column("psp_op"); + t_column* op_col = + process_state.m_flattened_data_table->_get_column("psp_op"); process_state.m_op_base = op_col->get_nth(0); t_column* pkey_col = - process_state.m_flattened_data_table->get_column("psp_pkey").get(); + process_state.m_flattened_data_table->_get_column("psp_pkey"); process_state.m_added_offset.resize(flattened_num_rows); process_state.m_prev_pkey_eq_vec.resize(flattened_num_rows); @@ -230,7 +230,7 @@ t_gnode::_process_mask_existed_rows(t_process_state& process_state) { prev_pkey.clear(); t_column* existed_column = - process_state.m_existed_data_table->get_column("psp_existed").get(); + process_state.m_existed_data_table->_get_column("psp_existed"); for (t_uindex idx = 0; idx < flattened_num_rows; ++idx) { t_tscalar pkey = pkey_col->get_scalar(idx); @@ -306,7 +306,7 @@ t_gnode::_process_table(t_uindex port_id) { t_uindex flattened_num_rows = flattened->num_rows(); std::vector row_lookup(flattened_num_rows); - t_column* pkey_col = flattened->get_column("psp_pkey").get(); + t_column* pkey_col = flattened->_get_column("psp_pkey"); for (t_uindex idx = 0; idx < flattened_num_rows; ++idx) { // See if each primary key in flattened already exist in the dataset @@ -377,18 +377,17 @@ t_gnode::_process_table(t_uindex port_id) { [&_process_state, &column_names, this](int colidx) { const std::string& cname = column_names[colidx]; auto* fcolumn = - _process_state.m_flattened_data_table->get_column(cname).get(); + _process_state.m_flattened_data_table->_get_column(cname); auto* scolumn = - _process_state.m_state_data_table->get_column(cname).get(); + _process_state.m_state_data_table->_get_column(cname); auto* dcolumn = - _process_state.m_delta_data_table->get_column(cname).get(); + _process_state.m_delta_data_table->_get_column(cname); auto* pcolumn = - _process_state.m_prev_data_table->get_column(cname).get(); + _process_state.m_prev_data_table->_get_column(cname); auto* ccolumn = - _process_state.m_current_data_table->get_column(cname).get(); + _process_state.m_current_data_table->_get_column(cname); auto* tcolumn = - _process_state.m_transitions_data_table->get_column(cname).get( - ); + _process_state.m_transitions_data_table->_get_column(cname); t_dtype col_dtype = fcolumn->get_dtype(); diff --git a/rust/perspective-server/cpp/perspective/src/cpp/gnode_state.cpp b/rust/perspective-server/cpp/perspective/src/cpp/gnode_state.cpp index d03433b3ab..755687bc6a 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/gnode_state.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/gnode_state.cpp @@ -97,7 +97,7 @@ t_gstate::lookup_or_create(const t_tscalar& pkey) { t_free_items::const_iterator iter = m_free.begin(); t_uindex idx = *iter; m_free.erase(iter); - m_mapping[pkey_] = idx; + m_mapping.emplace(pkey_, idx); return idx; } @@ -114,7 +114,7 @@ t_gstate::lookup_or_create(const t_tscalar& pkey) { m_table->set_size(nrows + 1); m_opcol->set_nth(nrows, OP_INSERT); m_pkcol->set_scalar(nrows, pkey); - m_mapping[pkey_] = nrows; + m_mapping.emplace(pkey_, nrows); return nrows; } @@ -127,9 +127,9 @@ t_gstate::fill_master_table(const t_data_table* flattened) { const t_schema& master_table_schema = m_table->get_schema(); const t_column* flattened_pkey_col = - flattened->get_const_column("psp_pkey").get(); + flattened->_get_const_column("psp_pkey"); const t_column* flattened_op_col = - flattened->get_const_column("psp_op").get(); + flattened->_get_const_column("psp_op"); t_uindex ncols = m_table->num_columns(); auto* master_table = m_table.get(); @@ -141,8 +141,8 @@ t_gstate::fill_master_table(const t_data_table* flattened) { const std::string& column_name = master_table_schema.m_columns[idx]; // No need for safe lookup as master_table schema == flattened // schema - auto flattened_column = - flattened->get_const_column_safe(column_name); + const t_column* flattened_column = + flattened->_get_const_column_safe(column_name); if (!flattened_column) { return; } @@ -156,16 +156,19 @@ t_gstate::fill_master_table(const t_data_table* flattened) { master_table->set_capacity(flattened->get_capacity()); master_table->set_size(flattened->size()); + const std::uint8_t* flattened_op_base = + flattened_op_col->get_nth_base(); for (t_uindex idx = 0, loop_end = flattened->num_rows(); idx < loop_end; ++idx) { t_tscalar pkey = flattened_pkey_col->get_scalar(idx); - const auto* op_ptr = flattened_op_col->get_nth(idx); - t_op op = static_cast(*op_ptr); + t_op op = static_cast(flattened_op_base[idx]); switch (op) { case OP_INSERT: { // Write new primary keys into `m_mapping` - m_mapping[m_symtable.get_interned_tscalar(pkey)] = idx; + m_mapping.emplace( + m_symtable.get_interned_tscalar(pkey), idx + ); m_opcol->set_nth(idx, OP_INSERT); m_pkcol->set_scalar(idx, pkey); } break; @@ -192,18 +195,19 @@ t_gstate::update_master_table(const t_data_table* flattened) { // Update existing `m_table` const t_column* flattened_pkey_col = - flattened->get_const_column("psp_pkey").get(); + flattened->_get_const_column("psp_pkey"); const t_column* flattened_op_col = - flattened->get_const_column("psp_op").get(); + flattened->_get_const_column("psp_op"); t_data_table* master_table = m_table.get(); std::vector master_table_indexes(flattened->num_rows()); + const std::uint8_t* flattened_op_base = + flattened_op_col->get_nth_base(); for (t_uindex idx = 0, loop_end = flattened->num_rows(); idx < loop_end; ++idx) { t_tscalar pkey = flattened_pkey_col->get_scalar(idx); - const auto* op_ptr = flattened_op_col->get_nth(idx); - t_op op = static_cast(*op_ptr); + t_op op = static_cast(flattened_op_base[idx]); switch (op) { case OP_INSERT: { @@ -240,15 +244,15 @@ t_gstate::update_master_table(const t_data_table* flattened) { this](int idx) { const std::string& column_name = master_schema.m_columns[idx]; t_column* master_column = - master_table->get_column(column_name).get(); - auto flattened_column = - flattened->get_const_column_safe(column_name); + master_table->_get_column(column_name); + const t_column* flattened_column = + flattened->_get_const_column_safe(column_name); if (!flattened_column) { return; } update_master_column( master_column, - flattened_column.get(), + flattened_column, flattened_op_col, master_table_indexes, flattened->num_rows() @@ -265,6 +269,7 @@ t_gstate::update_master_column( const std::vector& master_table_indexes, t_uindex num_rows ) { + const std::uint8_t* op_base = op_column->get_nth_base(); for (t_uindex idx = 0, loop_end = num_rows; idx < loop_end; ++idx) { bool is_valid = flattened_column->is_valid(idx); t_uindex master_table_idx = master_table_indexes[idx]; @@ -277,8 +282,7 @@ t_gstate::update_master_column( continue; } - const auto* op_ptr = op_column->get_nth(idx); - t_op op = static_cast(*op_ptr); + t_op op = static_cast(op_base[idx]); if (op == OP_DELETE) { continue; @@ -406,8 +410,7 @@ t_tscalar t_gstate::read_by_pkey( const t_data_table& table, const std::string& colname, t_tscalar& pkey ) const { - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); t_mapping::const_iterator iter = m_mapping.find(pkey); if (iter != m_mapping.end()) { return col_->get_scalar(iter->second); @@ -415,6 +418,24 @@ t_gstate::read_by_pkey( PSP_COMPLAIN_AND_ABORT("Called without pkey"); } +void +t_gstate::resolve_pkeys( + const std::vector& pkeys, + std::vector& out_row_indices +) const { + t_uindex num_rows = pkeys.size(); + out_row_indices.resize(num_rows); + constexpr t_uindex INVALID_ROW = t_uindex(-1); + for (t_uindex idx = 0; idx < num_rows; ++idx) { + t_mapping::const_iterator iter = m_mapping.find(pkeys[idx]); + if (iter != m_mapping.end()) { + out_row_indices[idx] = iter->second; + } else { + out_row_indices[idx] = INVALID_ROW; + } + } +} + void t_gstate::read_column( const t_data_table& table, @@ -423,18 +444,17 @@ t_gstate::read_column( std::vector& out_data ) const { t_index num_rows = pkeys.size(); - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); - std::vector rval(num_rows); + const t_column* col_ = table._get_const_column(colname); + out_data.resize(num_rows); for (t_index idx = 0; idx < num_rows; ++idx) { t_mapping::const_iterator iter = m_mapping.find(pkeys[idx]); if (iter != m_mapping.end()) { - rval[idx].set(col_->get_scalar(iter->second)); + out_data[idx].set(col_->get_scalar(iter->second)); + } else { + out_data[idx] = t_tscalar(); } } - - std::swap(rval, out_data); } void @@ -456,21 +476,19 @@ t_gstate::read_column( bool include_nones ) const { t_index num_rows = pkeys.size(); - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); - std::vector rval; - rval.reserve(num_rows); + out_data.clear(); + out_data.reserve(num_rows); for (t_index idx = 0; idx < num_rows; ++idx) { t_mapping::const_iterator iter = m_mapping.find(pkeys[idx]); if (iter != m_mapping.end()) { auto tscalar = col_->get_scalar(iter->second); if (include_nones || tscalar.is_valid()) { - rval.push_back(tscalar.to_double()); + out_data.push_back(tscalar.to_double()); } } } - std::swap(rval, out_data); } void @@ -488,18 +506,15 @@ t_gstate::read_column( return; } - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); - std::vector rval(num_rows); + out_data.resize(num_rows); t_uindex i = 0; for (t_uindex idx = start_idx; idx < end_idx; ++idx) { - rval[i] = col_->get_scalar(idx); + out_data[i] = col_->get_scalar(idx); i++; } - - std::swap(rval, out_data); } void @@ -509,19 +524,16 @@ t_gstate::read_column( const std::vector& row_indices, std::vector& out_data ) const { - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); t_index num_rows = row_indices.size(); - std::vector rval(num_rows); + out_data.resize(num_rows); t_uindex i = 0; for (auto idx : row_indices) { - rval[i] = col_->get_scalar(idx); + out_data[i] = col_->get_scalar(idx); i++; } - - std::swap(rval, out_data); } t_tscalar @@ -530,8 +542,7 @@ t_gstate::get( ) const { t_mapping::const_iterator iter = m_mapping.find(pkey); if (iter != m_mapping.end()) { - std::shared_ptr col = table.get_const_column(colname); - return col->get_scalar(iter->second); + return table._get_const_column(colname)->get_scalar(iter->second); } return {}; @@ -541,8 +552,7 @@ t_tscalar t_gstate::get_value( const t_data_table& table, const std::string& colname, const t_tscalar& pkey ) const { - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); t_tscalar rval = mknone(); auto iter = m_mapping.find(pkey); @@ -560,8 +570,7 @@ t_gstate::is_unique( const std::vector& pkeys, t_tscalar& value ) const { - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); value = mknone(); for (const auto& pkey : pkeys) { @@ -586,8 +595,7 @@ t_gstate::apply( t_tscalar& value, const std::function& fn ) const { - std::shared_ptr col = table.get_const_column(colname); - const t_column* col_ = col.get(); + const t_column* col_ = table._get_const_column(colname); value = mknone(); @@ -659,7 +667,7 @@ t_gstate::get_pkeyed_table( [&schema_columns, rval, table, &mask](int colidx) { const std::string& colname = schema_columns[colidx]; rval->set_column( - colname, table->get_const_column(colname)->clone(mask) + colname, table->_get_const_column(colname)->clone(mask) ); } @@ -683,11 +691,12 @@ t_gstate::get_row_data_pkeys(const std::vector& pkeys) const { t_uindex ncols = m_table->num_columns(); const t_schema& schema = m_table->get_schema(); std::vector rval; + rval.reserve(pkeys.size() * ncols); std::vector columns(ncols); for (t_uindex idx = 0, loop_end = schema.size(); idx < loop_end; ++idx) { const std::string& cname = schema.m_columns[idx]; - columns[idx] = m_table->get_const_column(cname).get(); + columns[idx] = m_table->_get_const_column(cname); } auto none = mknone(); diff --git a/rust/perspective-server/cpp/perspective/src/cpp/multi_sort.cpp b/rust/perspective-server/cpp/perspective/src/cpp/multi_sort.cpp index bd981f46b7..c414a36504 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/multi_sort.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/multi_sort.cpp @@ -47,34 +47,6 @@ t_mselem::t_mselem(const t_tscalar& pkey, const std::vector& row) : m_deleted(false), m_updated(false) {} -t_mselem::t_mselem(const t_mselem& other) { - m_pkey = other.m_pkey; - m_row = other.m_row; - m_deleted = other.m_deleted; - m_updated = other.m_updated; - m_order = other.m_order; -} - -t_mselem::t_mselem(t_mselem&& other) noexcept { - m_pkey = other.m_pkey; - m_row = std::move(other.m_row); - m_deleted = other.m_deleted; - m_updated = other.m_updated; - m_order = other.m_order; -} - -t_mselem& t_mselem::operator=(const t_mselem& other) = default; - -t_mselem& -t_mselem::operator=(t_mselem&& other) noexcept { - m_pkey = other.m_pkey; - m_row = std::move(other.m_row); - m_deleted = other.m_deleted; - m_updated = other.m_updated; - m_order = other.m_order; - return *this; -} - t_minmax_idx::t_minmax_idx(t_index mn, t_index mx) : m_min(mn), m_max(mx) {} // Given a vector return the indices of the @@ -112,19 +84,21 @@ get_minmax_idx(const std::vector& vec, t_sorttype stype) { } break; case SORTTYPE_ASCENDING_ABS: case SORTTYPE_DESCENDING_ABS: { - for (t_index idx = 0, loop_end = vec.size(); idx < loop_end; + double mindbl = std::abs(vec[0].to_double()); + double maxdbl = mindbl; + rval.m_min = 0; + rval.m_max = 0; + for (t_index idx = 1, loop_end = vec.size(); idx < loop_end; ++idx) { double val = std::abs(vec[idx].to_double()); - double mindbl = std::abs(double(min_max.first.as_bool())); - double maxdbl = std::abs(double(min_max.second.as_bool())); if (val <= mindbl) { - min_max.first.set(val); + mindbl = val; rval.m_min = idx; } if (val >= maxdbl) { - min_max.second.set(val); + maxdbl = val; rval.m_max = idx; } } @@ -215,7 +189,7 @@ t_multisorter::t_multisorter( const std::vector& order ) : m_sort_order(order), - m_elems(std::move(std::move(elems))) {} + m_elems(std::move(elems)) {} bool t_multisorter::operator()(const t_mselem& a, const t_mselem& b) const { diff --git a/rust/perspective-server/cpp/perspective/src/cpp/scalar.cpp b/rust/perspective-server/cpp/perspective/src/cpp/scalar.cpp index 967ae838cb..05291977c2 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/scalar.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/scalar.cpp @@ -43,6 +43,20 @@ operator>(const std::size_t& lhs, const t_tscalar& rhs) { if (!other.is_valid() || !is_valid()) { \ return rval; \ } \ + if (m_type == DTYPE_FLOAT64 && other.m_type == DTYPE_FLOAT64) { \ + rval.set(m_data.m_float64 OP other.m_data.m_float64); \ + return rval; \ + } \ + if (m_type == DTYPE_INT64 && other.m_type == DTYPE_INT64) { \ + rval.set(static_cast( \ + m_data.m_int64 OP other.m_data.m_int64)); \ + return rval; \ + } \ + if (m_type == DTYPE_INT32 && other.m_type == DTYPE_INT32) { \ + rval.set(static_cast( \ + m_data.m_int32 OP other.m_data.m_int32)); \ + return rval; \ + } \ rval.set(to_double() OP other.to_double()); \ return rval; diff --git a/rust/perspective-server/cpp/perspective/src/cpp/schema.cpp b/rust/perspective-server/cpp/perspective/src/cpp/schema.cpp index 3eb5311b86..987d3aa5d2 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/schema.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/schema.cpp @@ -115,12 +115,12 @@ t_schema::add_column(const std::string& colname, t_dtype dtype) { m_colidx_map[colname] = idx; m_coldt_map[colname] = dtype; - if (colname == std::string("psp_pkey")) { + if (colname == "psp_pkey") { m_pkeyidx = idx; m_is_pkey = true; } - if (colname == std::string("psp_op")) { + if (colname == "psp_op") { m_opidx = idx; m_is_pkey = true; } @@ -128,8 +128,8 @@ t_schema::add_column(const std::string& colname, t_dtype dtype) { void t_schema::retype_column(const std::string& colname, t_dtype dtype) { - if (colname == std::string("psp_pkey") - || colname == std::string("psp_op")) { + if (colname == "psp_pkey" + || colname == "psp_op") { PSP_COMPLAIN_AND_ABORT("Cannot retype primary key or operation columns." ); } diff --git a/rust/perspective-server/cpp/perspective/src/cpp/sparse_tree.cpp b/rust/perspective-server/cpp/perspective/src/cpp/sparse_tree.cpp index 116b97ccb0..8156a515d3 100644 --- a/rust/perspective-server/cpp/perspective/src/cpp/sparse_tree.cpp +++ b/rust/perspective-server/cpp/perspective/src/cpp/sparse_tree.cpp @@ -44,27 +44,22 @@ get_dominant(std::vector& values) { return mknone(); } - std::sort(values.begin(), values.end()); + // Single-pass O(n) frequency count using hash map instead of O(n log n) + // sort. + tsl::hopscotch_map freq; + freq.reserve(values.size()); t_tscalar delem = values[0]; - t_index dcount = 1; - t_index count = 1; + t_index dcount = 0; - for (t_index idx = 1, loop_end = values.size(); idx < loop_end; ++idx) { - const t_tscalar& prev = values[idx - 1]; - const t_tscalar& curr = values[idx]; - - if (curr == prev && curr.is_valid()) { - ++count; + for (const auto& val : values) { + if (!val.is_valid()) { + continue; } - - if ((idx + 1) == static_cast(values.size()) || curr != prev) { - if (count > dcount) { - delem = prev; - dcount = count; - } - - count = 1; + t_index count = ++freq[val]; + if (count > dcount) { + dcount = count; + delem = val; } } @@ -146,7 +141,7 @@ t_stree::init() { m_aggcols = std::vector(columns.size()); for (t_uindex idx = 0, loop_end = columns.size(); idx < loop_end; ++idx) { - m_aggcols[idx] = m_aggregates->get_const_column(columns[idx]).get(); + m_aggcols[idx] = m_aggregates->_get_const_column(columns[idx]); } m_deltas = std::make_shared(); @@ -194,7 +189,7 @@ t_stree::build_strand_table_phase_1( const std::vector& pivot_like ) const { pivots_neq = false; - std::set pivmap; + tsl::hopscotch_set pivmap; // if a row has been changed (value change, validity change, removed, etc.), // will be false. @@ -281,7 +276,7 @@ t_stree::build_strand_table_phase_2( t_uindex& insert_count, const std::vector& pivot_like ) const { - std::set pivmap; + tsl::hopscotch_set pivmap; // For each column, insert the prev value to the strand for (t_uindex pidx = 0, ploop_end = pivot_like.size(); pidx < ploop_end; @@ -432,10 +427,10 @@ t_stree::build_strand_table( // the construction method. for (t_uindex pidx = 0; pidx < npivotlike; ++pidx) { const std::string& piv = metadata.m_strand_schema.m_columns[pidx]; - piv_pcols[pidx] = prev.get_const_column(piv).get(); - piv_ccols[pidx] = current.get_const_column(piv).get(); - piv_tcols[pidx] = transitions.get_const_column(piv).get(); - piv_scols[pidx] = strands->get_column(piv).get(); + piv_pcols[pidx] = prev._get_const_column(piv); + piv_ccols[pidx] = current._get_const_column(piv); + piv_tcols[pidx] = transitions._get_const_column(piv); + piv_scols[pidx] = strands->_get_column(piv); } t_uindex aggcolsize = metadata.m_aggschema.m_columns.size(); @@ -454,17 +449,17 @@ t_stree::build_strand_table( agg_pcols[aggidx] = nullptr; strand_count_idx = aggidx; } else { - agg_dcols[aggidx] = delta.get_const_column(aggcol).get(); - agg_ccols[aggidx] = current.get_const_column(aggcol).get(); - agg_pcols[aggidx] = prev.get_const_column(aggcol).get(); + agg_dcols[aggidx] = delta._get_const_column(aggcol); + agg_ccols[aggidx] = current._get_const_column(aggcol); + agg_pcols[aggidx] = prev._get_const_column(aggcol); } - agg_acols[aggidx] = aggs->get_column(aggcol).get(); + agg_acols[aggidx] = aggs->_get_column(aggcol); } - t_column* agg_scount = aggs->get_column("psp_strand_count").get(); + t_column* agg_scount = aggs->_get_column("psp_strand_count"); - t_column* spkey = strands->get_column("psp_pkey").get(); + t_column* spkey = strands->_get_column("psp_pkey"); t_mask msk_prev; t_mask msk_curr; @@ -475,6 +470,7 @@ t_stree::build_strand_table( } bool has_filters = config.has_filters(); + const std::uint8_t* op_base = op_col->get_nth_base(); if (has_filters) { for (t_uindex idx = 0, loop_end = flattened.size(); idx < loop_end; @@ -483,7 +479,7 @@ t_stree::build_strand_table( bool filter_curr = msk_curr.get(idx); t_tscalar pkey = pkey_col->get_scalar(idx); - std::uint8_t op_ = *(op_col->get_nth(idx)); + std::uint8_t op_ = op_base[idx]; t_op op = static_cast(op_); bool pivots_neq; @@ -579,7 +575,7 @@ t_stree::build_strand_table( ++idx) { t_tscalar pkey = pkey_col->get_scalar(idx); - std::uint8_t op_ = *(op_col->get_nth(idx)); + std::uint8_t op_ = op_base[idx]; t_op op = static_cast(op_); bool pivots_neq; @@ -685,8 +681,8 @@ t_stree::build_strand_table( t_uindex insert_count = 0; for (t_uindex pidx = 0; pidx < npivotlike; ++pidx) { const std::string& piv = metadata.m_strand_schema.m_columns[pidx]; - piv_fcols[pidx] = flattened.get_const_column(piv).get(); - piv_scols[pidx] = strands->get_column(piv).get(); + piv_fcols[pidx] = flattened._get_const_column(piv); + piv_scols[pidx] = strands->_get_column(piv); } t_uindex aggcolsize = metadata.m_aggschema.m_columns.size(); @@ -699,14 +695,14 @@ t_stree::build_strand_table( agg_fcols[aggidx] = nullptr; strand_count_idx = aggidx; } else { - agg_fcols[aggidx] = flattened.get_const_column(aggcol).get(); + agg_fcols[aggidx] = flattened._get_const_column(aggcol); } - agg_acols[aggidx] = aggs->get_column(aggcol).get(); + agg_acols[aggidx] = aggs->_get_column(aggcol); } - t_column* agg_scount = aggs->get_column("psp_strand_count").get(); - t_column* spkey = strands->get_column("psp_pkey").get(); + t_column* agg_scount = aggs->_get_column("psp_strand_count"); + t_column* spkey = strands->_get_column("psp_pkey"); t_mask msk; if (config.has_filters()) { msk = filter_table_for_config(flattened, config); @@ -716,6 +712,7 @@ t_stree::build_strand_table( const t_uindex loop_end = flattened.size(); const t_uindex size = loop_end - msk.count(); const t_uindex ploop_end = metadata.m_pivot_like_columns.size(); + const std::uint8_t* op_base = op_col->get_nth_base(); parallel_for(int(aggcolsize + 1), [&](int aggidx) { // This over-allocates for `OP_DELETE`, as it only accounts for // filtered count. @@ -736,7 +733,7 @@ t_stree::build_strand_table( continue; } - std::uint8_t op_ = *(op_col->get_nth(idx)); + std::uint8_t op_ = op_base[idx]; t_op op = static_cast(op_); if (op == OP_DELETE) { continue; @@ -798,7 +795,7 @@ t_stree::populate_pkey_idx( auto pkey = m_symtable.get_interned_tscalar(pkey_col->get_scalar(lfidx)); auto strand_count = - *(strand_count_col->get_nth(lfidx)); + strand_count_col->get_nth_base()[lfidx]; // Checks the strand count and adds a new primary key if it's // increased. @@ -982,9 +979,9 @@ t_stree::update_aggs_from_static( for (const auto& colname : aggschema.m_columns) { agg_update_info.m_src.push_back( - src_aggtable.get_const_column(colname).get() + src_aggtable._get_const_column(colname) ); - agg_update_info.m_dst.push_back(m_aggregates->get_column(colname).get() + agg_update_info.m_dst.push_back(m_aggregates->_get_column(colname) ); agg_update_info.m_aggspecs.push_back(ctx.get_aggspec(colname)); } @@ -2428,7 +2425,7 @@ t_stree::get_aggregate(t_index idx, t_index aggnum) const { } auto aggtable = get_aggtable(); - const auto* c = aggtable->get_const_column(aggnum).get(); + const auto* c = aggtable->_get_const_column(aggnum); auto agg_ridx = get_aggidx(idx); t_index pidx = get_parent_idx(idx); diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/aggregate.h b/rust/perspective-server/cpp/perspective/src/include/perspective/aggregate.h index cac8bc4acc..22b1e95b6d 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/aggregate.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/aggregate.h @@ -46,15 +46,23 @@ class PERSPECTIVE_EXPORT t_aggimpl_sum ROLLING_T reduce(const RAW_DATA_T* biter, const RAW_DATA_T* eiter) { - ROLLING_T value = - std::accumulate(biter, eiter, static_cast(0)); + ROLLING_T value = static_cast(0); + auto n = eiter - biter; +#pragma omp simd reduction(+ : value) + for (decltype(n) i = 0; i < n; ++i) { + value += static_cast(biter[i]); + } return value; } ROLLING_T roll_up(const ROLLING_T* biter, const ROLLING_T* eiter) { - ROLLING_T value = - std::accumulate(biter, eiter, static_cast(0)); + ROLLING_T value = static_cast(0); + auto n = eiter - biter; +#pragma omp simd reduction(+ : value) + for (decltype(n) i = 0; i < n; ++i) { + value += biter[i]; + } return value; } }; @@ -119,27 +127,29 @@ class PERSPECTIVE_EXPORT t_aggimpl_mean ROLLING_T reduce(const RAW_DATA_T* biter, const RAW_DATA_T* eiter) { + double sum = 0.0; + auto n = eiter - biter; +#pragma omp simd reduction(+ : sum) + for (decltype(n) i = 0; i < n; ++i) { + sum += static_cast(biter[i]); + } - double sum = std::accumulate(biter, eiter, static_cast(0)); - - double count = eiter - biter; - + double count = static_cast(n); return ROLLING_T(sum, count); } ROLLING_T roll_up(const ROLLING_T* biter, const ROLLING_T* eiter) { + double sum = 0.0; + double count = 0.0; + auto niter = eiter - biter; - ROLLING_T value(0, 0); - t_uindex niter = eiter - biter; - - for (t_uindex idx = 0; idx < niter; ++idx) { - const ROLLING_T* tmp = biter + idx; - value.first += tmp->first; - value.second += tmp->second; + for (decltype(niter) idx = 0; idx < niter; ++idx) { + sum += biter[idx].first; + count += biter[idx].second; } - return value; + return ROLLING_T(sum, count); } }; @@ -225,8 +235,9 @@ build_aggregate_helper( t_index nidx ) { typedef typename AGGIMPL_T::t_rolling t_rolling; - const t_rolling* biter = ocolumn->get_nth(bcidx); - const t_rolling* eiter = ocolumn->get_nth(ecidx); + const t_rolling* base = ocolumn->get_nth_base(); + const t_rolling* biter = base + bcidx; + const t_rolling* eiter = base + ecidx; t_rolling rolling = aggimpl.roll_up(biter, eiter); ocolumn->set_nth(nidx, rolling); } @@ -287,10 +298,12 @@ t_aggregate::build_aggregate() { return; } - std::vector buffer(icptr_size); const t_column* lcptr = m_tree.get_leaf_cptr(); const t_uindex* base_lcptr = lcptr->get(0); + // Lazily sized buffer — allocated to max leaf range, not entire column. + std::vector buffer; + for (t_index level_idx = n_levels; level_idx > -1; level_idx--) { std::pair markers = m_tree.get_level_markers(level_idx); @@ -307,10 +320,15 @@ t_aggregate::build_aggregate() { PSP_VERBOSE_ASSERT(elptr > blptr, "Unexpected pointers"); + t_uindex leaf_count = elptr - blptr; + if (buffer.size() < leaf_count) { + buffer.resize(leaf_count); + } + icptr->fill(buffer, blptr, elptr); t_raw_data* biter = &buffer[0]; - t_raw_data* eiter = biter + (elptr - blptr); + t_raw_data* eiter = biter + leaf_count; auto tmp = aggimpl.reduce(biter, eiter); ocolumn->set_nth(nidx, tmp); } diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/arg_sort.h b/rust/perspective-server/cpp/perspective/src/include/perspective/arg_sort.h index 8cf9ada7df..e172c53c39 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/arg_sort.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/arg_sort.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace perspective { @@ -23,6 +24,57 @@ struct t_multisorter; PERSPECTIVE_EXPORT void argsort(std::vector& output, const t_multisorter& sorter); +template +struct t_argsort_comparator_impl { + explicit t_argsort_comparator_impl(const std::vector& v) : + m_v(v) {} + + bool operator()(t_index a, t_index b) const; + + const std::vector& m_v; +}; + +template <> +inline bool +t_argsort_comparator_impl::operator()( + t_index a, t_index b +) const { + return m_v[a] < m_v[b]; +} + +template <> +inline bool +t_argsort_comparator_impl::operator()( + t_index a, t_index b +) const { + return m_v[a] > m_v[b]; +} + +template <> +inline bool +t_argsort_comparator_impl::operator()( + t_index a, t_index b +) const { + return std::abs(m_v[a].to_double()) < std::abs(m_v[b].to_double()); +} + +template <> +inline bool +t_argsort_comparator_impl::operator()( + t_index a, t_index b +) const { + return std::abs(m_v[a].to_double()) > std::abs(m_v[b].to_double()); +} + +template <> +inline bool +t_argsort_comparator_impl::operator()( + t_index a, t_index b +) const { + return a < b; +} + +// Legacy non-template comparator kept for API compatibility struct PERSPECTIVE_EXPORT t_argsort_comparator { t_argsort_comparator( const std::vector& v, const t_sorttype& sort_type diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/column.h b/rust/perspective-server/cpp/perspective/src/include/perspective/column.h index 3703258988..a3a593d4b3 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/column.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/column.h @@ -106,6 +106,14 @@ class PERSPECTIVE_EXPORT t_column { template const T* get_nth(t_uindex idx) const; + // Returns a raw pointer to the beginning of the typed data array. + // Use for sequential/batched access to avoid per-element get_nth calls. + template + const T* get_nth_base() const; + + template + T* get_nth_base(); + // idx is in items const t_status* get_nth_status(t_uindex idx) const; @@ -302,6 +310,18 @@ t_column::get_nth(t_uindex idx) const { return m_data->get_nth(idx); } +template +const T* +t_column::get_nth_base() const { + return m_data->get_nth(0); +} + +template +T* +t_column::get_nth_base() { + return m_data->get_nth(0); +} + template T* t_column::extend() { diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/context_two.h b/rust/perspective-server/cpp/perspective/src/include/perspective/context_two.h index 1d5942b5fb..b93d3089ff 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/context_two.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/context_two.h @@ -68,6 +68,10 @@ class PERSPECTIVE_EXPORT t_ctx2 : public t_ctxbase { resolve_cells(const std::vector>& cells ) const; + std::vector + resolve_cells(t_uindex srow, t_uindex erow, t_uindex scol, t_uindex ecol + ) const; + std::shared_ptr rtree(); std::shared_ptr rtree() const; diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/data_table.h b/rust/perspective-server/cpp/perspective/src/include/perspective/data_table.h index b9bd9b02c8..e6a0146d1e 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/data_table.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/data_table.h @@ -126,6 +126,9 @@ class PERSPECTIVE_EXPORT t_data_table { void set_table_size(t_uindex size); t_column* _get_column(std::string_view colname); + const t_column* _get_const_column(std::string_view colname) const; + const t_column* _get_const_column_safe(std::string_view colname) const; + const t_column* _get_const_column(t_uindex idx) const; std::shared_ptr flatten() const; @@ -367,16 +370,16 @@ t_data_table::flatten_helper_1(FLATTENED_T flattened) const { for (const auto& colname : m_schema.m_columns) { if (colname != "psp_pkey" && colname != "psp_op") { - s_columns.push_back(get_const_column(colname).get()); - d_columns.push_back(flattened->get_column(colname).get()); + s_columns.push_back(_get_const_column(colname)); + d_columns.push_back(flattened->_get_column(colname)); } } - const t_column* s_pkey_col = get_const_column("psp_pkey").get(); - const t_column* s_op_col = get_const_column("psp_op").get(); + const t_column* s_pkey_col = _get_const_column("psp_pkey"); + const t_column* s_op_col = _get_const_column("psp_op"); - t_column* d_pkey_col = flattened->get_column("psp_pkey").get(); - t_column* d_op_col = flattened->get_column("psp_op").get(); + t_column* d_pkey_col = flattened->_get_column("psp_pkey"); + t_column* d_op_col = flattened->_get_column("psp_op"); typedef std::vector> t_rpvec; @@ -554,7 +557,7 @@ t_data_table::flatten_helper_1(FLATTENED_T flattened) const { int(m_schema.get_num_columns()), [&flattened, this](int colidx) { const auto& colname = this->m_schema.m_columns[colidx]; - auto col = get_const_column(colname).get(); + auto col = _get_const_column(colname); if (col->get_dtype() == DTYPE_STR) { flattened->get_column(colname)->copy_vocabulary(col); } diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/gnode_state.h b/rust/perspective-server/cpp/perspective/src/include/perspective/gnode_state.h index 52c797df31..391329653a 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/gnode_state.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/gnode_state.h @@ -104,6 +104,11 @@ class PERSPECTIVE_EXPORT t_gstate { // a `t_data_table` - usually a pointer to the gnode state's master // table or the expression table from the context. + void resolve_pkeys( + const std::vector& pkeys, + std::vector& out_row_indices + ) const; + t_tscalar read_by_pkey( const t_data_table& table, const std::string& colname, t_tscalar& pkey ) const; diff --git a/rust/perspective-server/cpp/perspective/src/include/perspective/multi_sort.h b/rust/perspective-server/cpp/perspective/src/include/perspective/multi_sort.h index 9aef56abc3..be546d1a48 100644 --- a/rust/perspective-server/cpp/perspective/src/include/perspective/multi_sort.h +++ b/rust/perspective-server/cpp/perspective/src/include/perspective/multi_sort.h @@ -16,21 +16,127 @@ #include #include #include +#include #include namespace perspective { +// Inline fixed-capacity vector for sort rows with small-buffer optimization. +// Uses a stack-allocated array for <= SBO_CAPACITY elements, falling back to +// heap allocation for larger sizes. All access is branchless via pointer +// indirection; the inline-vs-heap decision is made once at reserve() time. +struct PERSPECTIVE_EXPORT t_sortrow_vec { + static constexpr std::size_t SBO_CAPACITY = 8; + + t_sortrow_vec() : m_ptr(m_data.data()), m_alloc(nullptr), m_size(0) {} + + t_sortrow_vec(const std::vector& v) + : m_alloc(nullptr), m_size(v.size()) { + if (m_size > SBO_CAPACITY) { + m_alloc = new t_tscalar[m_size]; + m_ptr = m_alloc; + } else { + m_ptr = m_data.data(); + } + for (std::size_t i = 0; i < m_size; ++i) { + m_ptr[i] = v[i]; + } + } + + ~t_sortrow_vec() { delete[] m_alloc; } + + t_sortrow_vec(const t_sortrow_vec& o) + : m_data(o.m_data), m_alloc(nullptr), m_size(o.m_size) { + if (o.m_alloc) { + m_alloc = new t_tscalar[m_size]; + std::copy(o.m_ptr, o.m_ptr + m_size, m_alloc); + m_ptr = m_alloc; + } else { + m_ptr = m_data.data(); + } + } + + t_sortrow_vec& operator=(const t_sortrow_vec& o) { + if (this != &o) { + delete[] m_alloc; + m_alloc = nullptr; + m_size = o.m_size; + if (o.m_alloc) { + m_alloc = new t_tscalar[m_size]; + std::copy(o.m_ptr, o.m_ptr + m_size, m_alloc); + m_ptr = m_alloc; + } else { + m_data = o.m_data; + m_ptr = m_data.data(); + } + } + return *this; + } + + t_sortrow_vec(t_sortrow_vec&& o) noexcept + : m_data(o.m_data), m_alloc(o.m_alloc), m_size(o.m_size) { + if (m_alloc) { + m_ptr = m_alloc; + } else { + m_ptr = m_data.data(); + } + o.m_alloc = nullptr; + o.m_size = 0; + o.m_ptr = o.m_data.data(); + } + + t_sortrow_vec& operator=(t_sortrow_vec&& o) noexcept { + if (this != &o) { + delete[] m_alloc; + m_data = o.m_data; + m_alloc = o.m_alloc; + m_size = o.m_size; + if (m_alloc) { + m_ptr = m_alloc; + } else { + m_ptr = m_data.data(); + } + o.m_alloc = nullptr; + o.m_size = 0; + o.m_ptr = o.m_data.data(); + } + return *this; + } + + void reserve(std::size_t capacity) { + if (capacity > SBO_CAPACITY && !m_alloc) { + m_alloc = new t_tscalar[capacity]; + m_ptr = m_alloc; + } + } + + void push_back(const t_tscalar& v) { m_ptr[m_size++] = v; } + + const t_tscalar& operator[](std::size_t i) const { return m_ptr[i]; } + t_tscalar& operator[](std::size_t i) { return m_ptr[i]; } + + std::size_t size() const { return m_size; } + + const t_tscalar* begin() const { return m_ptr; } + const t_tscalar* end() const { return m_ptr + m_size; } + + std::array m_data; + t_tscalar* m_ptr; + t_tscalar* m_alloc; + std::uint8_t m_size; +}; + struct PERSPECTIVE_EXPORT t_mselem { t_mselem(); t_mselem(const std::vector& row); t_mselem(const std::vector& row, t_uindex order); t_mselem(const t_tscalar& pkey, const std::vector& row); - t_mselem(const t_mselem& other); - t_mselem(t_mselem&& other) noexcept; - t_mselem& operator=(const t_mselem& other); - t_mselem& operator=(t_mselem&& other) noexcept; + t_mselem(const t_mselem& other) = default; + t_mselem(t_mselem&& other) noexcept = default; + t_mselem& operator=(const t_mselem& other) = default; + t_mselem& operator=(t_mselem&& other) noexcept = default; - std::vector m_row; + t_sortrow_vec m_row; t_tscalar m_pkey; t_uindex m_order; bool m_deleted; @@ -41,6 +147,19 @@ struct PERSPECTIVE_EXPORT t_mselem { namespace std { +inline std::ostream& +operator<<(std::ostream& os, const perspective::t_sortrow_vec& v) { + os << "["; + for (std::size_t i = 0; i < v.size(); ++i) { + if (i > 0) { + os << ", "; + } + os << v[i]; + } + os << "]"; + return os; +} + inline std::ostream& operator<<(std::ostream& os, const perspective::t_mselem& t) { os << "mse " << t.m_pkey << " row => " << t.m_row << " deleted => " @@ -107,20 +226,22 @@ cmp_mselem( t_sorttype order = sort_order[idx]; - t_nancmp nancmp = nan_compare(order, first, second); - - if (first.is_floating_point() && nancmp.m_active) { - switch (nancmp.m_cmpval) { - case CMP_OP_LT: { - return true; - } break; - case CMP_OP_GT: { - return false; - } break; - case CMP_OP_EQ: - default: { - continue; - } break; + if (first.is_floating_point() || second.is_floating_point()) { + t_nancmp nancmp = nan_compare(order, first, second); + + if (nancmp.m_active) { + switch (nancmp.m_cmpval) { + case CMP_OP_LT: { + return true; + } break; + case CMP_OP_GT: { + return false; + } break; + case CMP_OP_EQ: + default: { + continue; + } break; + } } } diff --git a/tools/bench/package.json b/tools/bench/package.json index c6055fec26..bf02a8745f 100644 --- a/tools/bench/package.json +++ b/tools/bench/package.json @@ -26,6 +26,9 @@ "zx": "catalog:" }, "dependencies": { + "perspective-4-2-0": "npm:@perspective-dev/client@4.2.0", + "perspective-4-1-0": "npm:@perspective-dev/client@4.1.0", + "perspective-4-0-0": "npm:@perspective-dev/client@4.0.0", "perspective-3-8-0": "npm:@finos/perspective@3.8.0", "perspective-3-7-0": "npm:@finos/perspective@3.7.0", "perspective-3-6-0": "npm:@finos/perspective@3.6.0", diff --git a/tools/bench/src/html/index.html b/tools/bench/src/html/index.html index b337d7c4ab..d6abdb3aa8 100644 --- a/tools/bench/src/html/index.html +++ b/tools/bench/src/html/index.html @@ -2,12 +2,9 @@ - - - - -