@@ -563,11 +563,44 @@ void Join::addCombinedRowToIdTable(const ROW_A& rowA, const ROW_B& rowB,
563563 }
564564}
565565
566+ // _____________________________________________________________________________
567+ namespace {
568+ // Type alias for the general InputRangeTypeErased with specific types.
569+ using IteratorWithSingleCol = InputRangeTypeErased<IdTableAndFirstCol<IdTable>>;
570+
571+ // Convert a `CompressedRelationReader::IdTableGeneratorInputRange` to a
572+ // `InputRangeTypeErased<IdTableAndFirstCol<IdTable>>` for more efficient access
573+ // in the join columns below. This also makes sure the runtime information of
574+ // the passed `IndexScan` is updated properly as the range is consumed.
575+ IteratorWithSingleCol convertGenerator (
576+ CompressedRelationReader::IdTableGeneratorInputRange gen, IndexScan& scan,
577+ bool postUpdates) {
578+ // Store the generator in a wrapper so we can access its details after moving
579+ auto generatorStorage =
580+ std::make_shared<CompressedRelationReader::IdTableGeneratorInputRange>(
581+ std::move (gen));
582+
583+ auto range = CachingTransformInputRange (
584+ *generatorStorage, [generatorStorage, &scan, postUpdates,
585+ first = true ](auto & table) mutable {
586+ scan.updateRuntimeInfoForLazyScan (generatorStorage->details (),
587+ first || postUpdates);
588+ first = false ;
589+ // IndexScans don't have a local vocabulary, so we can just use an empty
590+ // one.
591+ return IdTableAndFirstCol{std::move (table), LocalVocab{}};
592+ });
593+
594+ return IteratorWithSingleCol{std::move (range)};
595+ }
596+ } // namespace
597+
566598// ______________________________________________________________________________________________________
567599Result Join::computeResultForTwoIndexScans (bool requestLaziness) const {
568600 return createResult (
569601 requestLaziness,
570- [this ](std::function<void (IdTable&, LocalVocab&)> yieldTable) {
602+ [this ,
603+ requestLaziness](std::function<void (IdTable&, LocalVocab&)> yieldTable) {
571604 auto leftScan =
572605 std::dynamic_pointer_cast<IndexScan>(_left->getRootOperation ());
573606 auto rightScan =
@@ -585,15 +618,17 @@ Result Join::computeResultForTwoIndexScans(bool requestLaziness) const {
585618 IndexScan::lazyScanForJoinOfTwoScans (*leftScan, *rightScan);
586619 runtimeInfo ().addDetail (" time-for-filtering-blocks" , timer.msecs ());
587620
588- auto leftBlocks = convertGenerator (std::move (leftBlocksInternal));
589- auto rightBlocks = convertGenerator (std::move (rightBlocksInternal));
621+ // If requestLaziness, we don't need to serialize json for every update
622+ // of the child. If we serialize it whenever the join operation yields a
623+ // table that's frequent enough and reduces the overhead.
624+ auto leftBlocks = convertGenerator (std::move (leftBlocksInternal),
625+ *leftScan, !requestLaziness);
626+ auto rightBlocks = convertGenerator (std::move (rightBlocksInternal),
627+ *rightScan, !requestLaziness);
590628
591629 ad_utility::zipperJoinForBlocksWithoutUndef (leftBlocks, rightBlocks,
592630 std::less{}, rowAdder);
593631
594- leftScan->updateRuntimeInfoForLazyScan (leftBlocks.details ());
595- rightScan->updateRuntimeInfoForLazyScan (rightBlocks.details ());
596-
597632 auto localVocab = std::move (rowAdder.localVocab ());
598633 return Result::IdTableVocabPair{std::move (rowAdder).resultTable (),
599634 std::move (localVocab)};
@@ -611,7 +646,7 @@ Result Join::computeResultForIndexScanAndIdTable(
611646 auto resultPermutation = joinColMap.permutationResult ();
612647 return createResult (
613648 requestLaziness,
614- [this , scan = std::move (scan),
649+ [this , requestLaziness, scan = std::move (scan),
615650 resultWithIdTable = std::move (resultWithIdTable),
616651 joinColMap = std::move (joinColMap)](
617652 std::function<void (IdTable&, LocalVocab&)> yieldTable) {
@@ -632,9 +667,9 @@ Result Join::computeResultForIndexScanAndIdTable(
632667 .isUndefined ();
633668 std::optional<std::shared_ptr<const Result>> indexScanResult =
634669 std::nullopt ;
635- auto rightBlocks = [&scan, idTableHasUndef, &permutationIdTable ,
636- &indexScanResult]()
637- -> std::variant< LazyInputView, GeneratorWithDetails> {
670+ auto rightBlocks = [requestLaziness, &scan, idTableHasUndef,
671+ &permutationIdTable,
672+ &indexScanResult]() -> LazyInputView {
638673 if (idTableHasUndef) {
639674 indexScanResult =
640675 scan->getResult (false , ComputationMode::LAZY_IF_SUPPORTED);
@@ -644,7 +679,8 @@ Result Join::computeResultForIndexScanAndIdTable(
644679 } else {
645680 auto rightBlocksInternal =
646681 scan->lazyScanForJoinOfColumnWithScan (permutationIdTable.col ());
647- return convertGenerator (std::move (rightBlocksInternal));
682+ return convertGenerator (std::move (rightBlocksInternal), *scan,
683+ !requestLaziness);
648684 }
649685 }();
650686
@@ -657,19 +693,10 @@ Result Join::computeResultForIndexScanAndIdTable(
657693 left, right, std::less{}, rowAdder);
658694 };
659695 auto blockForIdTable = std::array{std::move (permutationIdTable)};
660- std::visit (
661- [&doJoin, &blockForIdTable](auto & blocks) {
662- if constexpr (idTableIsRightInput) {
663- doJoin (blocks, blockForIdTable);
664- } else {
665- doJoin (blockForIdTable, blocks);
666- }
667- },
668- rightBlocks);
669-
670- if (std::holds_alternative<GeneratorWithDetails>(rightBlocks)) {
671- scan->updateRuntimeInfoForLazyScan (
672- std::get<GeneratorWithDetails>(rightBlocks).details ());
696+ if constexpr (idTableIsRightInput) {
697+ doJoin (rightBlocks, blockForIdTable);
698+ } else {
699+ doJoin (blockForIdTable, rightBlocks);
673700 }
674701
675702 auto localVocab = std::move (rowAdder.localVocab ());
0 commit comments