Source code for srdatalog.ir.codegen.cuda.bitmap
'''Execute-only CUDA runner for exact binary projection bitmap plans.'''
from __future__ import annotations
import srdatalog.ir.mir.types as m
from srdatalog.ir.hir.types import Version
def _bind_relation(source: m.ColumnSource, name: str) -> str:
full = f'get_relation_by_schema<{source.rel_name}, FULL_VER>(db)'
if source.version is Version.DELTA:
relation = (
f'(iteration == 0) ? {full} : get_relation_by_schema<{source.rel_name}, DELTA_VER>(db)'
)
elif source.version is Version.FULL:
relation = full
else:
raise ValueError('dedup_bitmap sources must use FULL or DELTA')
cols = ', '.join(map(str, source.index))
return (
f' auto& {name}_relation = {relation};\n'
f' const auto& {name}_index = {name}_relation.get_index(SRDatalog::IndexSpec{{{{{cols}}}}});\n'
)
[docs]
def gen_bitmap_runner(
node: m.ExecutePipeline,
db_type_name: str,
rel_index_types: dict[str, str],
) -> tuple[str, str]:
'''Emit an exact set-projection runner, without hash or ordinary join kernels.'''
plan = node.bitmap_join
if plan is None or len(node.dest_specs) != 1:
raise ValueError('dedup_bitmap requires one binary destination')
if node.count or node.dedup_hash or node.work_stealing or node.block_group or node.use_fan_out:
raise ValueError('dedup_bitmap cannot be combined with other execution strategies')
dest = node.dest_specs[0]
if dest.version is not Version.NEW or sorted(dest.index) != [0, 1] or len(dest.vars) != 2:
raise ValueError('dedup_bitmap requires a binary NEW destination')
for source in (plan.assign, plan.points):
if sorted(source.index) != [0, 1] or source.prefix_vars:
raise ValueError('dedup_bitmap requires unconstrained binary source indexes')
index_type = rel_index_types.get(source.rel_name, '')
if index_type and not any(
t in index_type for t in ('DeviceSortedArrayIndex', 'Device2LevelIndex')
):
raise ValueError(f'dedup_bitmap does not support index type {index_type!r}')
runner = f'JitRunner_{node.rule_name}'
declaration = (
f'struct {runner} {{\n'
f' using DB = {db_type_name};\n'
' static void execute(DB& db, uint32_t iteration);\n'
'};\n\n'
)
dictionary = ', '.join(map(str, reversed(plan.points.index)))
body = f'''void {runner}::execute(DB& db, uint32_t iteration) {{
nvtxRangePushA("{node.rule_name}");
struct RangeEnd {{ ~RangeEnd() {{ nvtxRangePop(); }} }} range_end;
namespace bitmap = SRDatalog::GPU::bitmap;
bitmap::Input input{{}};
auto columns = [](const auto& index) -> bitmap::Columns {{
using Index = std::remove_cvref_t<decltype(index)>;
static_assert(Index::arity == 2 && std::is_same_v<typename Index::ValueType, uint32_t>);
if (index.size() == 0) return {{}};
return {{index.size(), index.data().template column_ptr<0>(),
index.data().template column_ptr<1>()}};
}};
auto segments = [&](const auto& index, auto& full, auto& head) {{
if constexpr (requires {{ index.full(); index.head(); }}) {{
full = columns(index.full());
head = columns(index.head());
}} else {{
full = columns(index);
}}
}};
auto keys = [](const auto& index) -> bitmap::Keys {{
if (index.size() == 0) return {{}};
if (index.num_unique_root_values() == 0)
throw std::runtime_error("dedup_bitmap: nonempty index has no value-key cache");
return {{index.num_unique_root_values(), index.root_unique_values().data()}};
}};
'''
body += _bind_relation(plan.assign, 'assign')
body += _bind_relation(plan.points, 'points')
body += f''' segments(assign_index, input.assign, input.assign_head);
segments(points_index, input.points_full, input.points_head);
const auto& value_index = points_relation.get_index(SRDatalog::IndexSpec{{{{{dictionary}}}}});
auto collect_keys = [&](const auto& index) {{
if constexpr (requires {{ index.full(); index.head(); }}) {{
input.heaps_full = keys(index.full());
input.heaps_head = keys(index.head());
}} else {{
input.heaps_full = keys(index);
}}
}};
collect_keys(value_index);
auto& destination = get_relation_by_schema<{dest.rel_name}, NEW_VER>(db);
using Destination = std::remove_reference_t<decltype(destination)>;
static_assert(!has_provenance_v<typename Destination::semiring_type>);
bitmap::execute(input, [&](uint64_t rows) -> bitmap::Output {{
const uint64_t old_rows = destination.size();
constexpr uint64_t limit = std::numeric_limits<uint32_t>::max();
if (old_rows > limit || rows > limit - old_rows)
throw std::overflow_error("dedup_bitmap: NEW relation exceeds uint32 row limit");
destination.resize_interned_columns(static_cast<std::size_t>(old_rows + rows), 0);
return {{destination.template interned_column<0>() + old_rows,
destination.template interned_column<1>() + old_rows}};
}});
}}
'''
return declaration, '#include "gpu/runtime/jit/bitmap_join.h"\n\n' + declaration + body