// Take the short query above to demonstrate how to use L3 APIs. // create GQE instance to handle card management // with shell name, path to xclbin file to be loaded, operator combination gqe::workshop wksp("xilinx_u50_gen3x16_xdma_5_202210_1", "./gqe_join.xclbin", gqe::WorkerFunctions::JOIN); // Define input table schema, including attributes and size of each attribute's element. // Also setup if enable rowid and validation bits or not. // rowid is an auto generated attribute inside kernel. // validation bits represent if valid for each input rows. // Table O has 1 column, of type int64_t, attribute name is "o_rowid", rowID and validation bits enabled, attribute name is "o_rowid", "o_valid" // Table L has 1 column, of type int64_t, attribute name is "l_rowid", rowID and validation bits enabled, attribute name is "l_rowid", "l_valid" // Table C has 3 column, of type int64_t, attribute name is "c1", "c2", "c3", has no rowid and validation bits" gqe::TableSection tab_o("Table O", {"o_orderkey"}, {sizeof(int64_t)}, 1, valid_o, "o_rowid", "o_valid"); gqe::TableSection tab_l("Table L", {"l_orderkey"}, {sizeof(int64_t)}, 1, valid_l, "l_rowid", "l_valid"); gqe::TableSection tab_c("Table C", {"c1", "c2", "c3"}, {sizeof(int64_t), sizeof(int64_t), sizeof(int64_t)}, 0, 0, "", ""); // After the schema of input table has been defined, add rows by section. // Each section added to TableSection should contains exactly same number of columns in the exact same order of attribute. // This only add pointers, which should been valid no later than this section's corresponding future is ready. // tab_o_col0, tab_l_col0, tab_c_col0, tab_c_col1, tab_c_col2, tab_c_col3 are pointer to input and output memory // tab_o_valid, tab_l_valid is the validation bits for table O and table L, represent filter result from previous processing, as input. // tab_part_o_col0, tab_part_l_col0 are pointer to intermediate partition result. tab_o.addSec(vector<char*>({(char*)tab_o_col0}), tab_o_valid, table_o_nrow); tab_l.addSec(vector<char*>({(char*)tab_l_col0}), tab_l_valid, table_l_nrow); tab_c.addSec(vector<char*>({(char*)tab_c_col0, (char*)tab_c_col1, (char*)tab_c_col2, (char*)tab_c_col3}, nullptr, table_c_nrow); tab_part_o.addSec(vector<char*>({(char*)tab_part_o_col0}), nullptr, table_o_nrow * 2); tab_part_l.addSec(vector<char*>({(char*)tab_part_l_col0}), nullptr, table_l_nrow * 2); // intermeidiate result will be stored in "tab_part_o" and "tab_part_l". // schema definition and adding section are similar gqe::TableSection tab_part_o("Part O", {"o1", "o2", "o3"}, {sizeof(int64_t), sizeof(int64_t), sizeof(int64_t)}, 0, 0, "", ""); gqe::TableSection tab_part_l("Part L", {"l1", "l2", "l3"}, {sizeof(int64_t), sizeof(int64_t), sizeof(int64_t)}, 0, 0, "", ""); tab_part_o.addSec({((char*)tmp_o1), ((char*)tmp_o2), ((char*)tmp_o3)}, nullptr, d_part_o_nrow); tab_part_l.addSec({((char*)tmp_l1), ((char*)tmp_l2), ((char*)tmp_l3)}, nullptr, d_part_l_nrow); // Call GQE API to perform "filter + hash partition + hash join" // This will merge the original execution plan tree into 1 operations. // Table O is filtered with "o_rowid>0" // No filter and only partition on table L. // To support bigger input tableSection, GQE will also perform hash partition on Table O and Table L. // Join table O and table L with "o_orderkey = l_orderkey", output 3 columns of l_orderkey, o_rowid and l_rowid. // Join will return imediately wksp.Join(&tab_o, "o_rowid>0", &tab_o_ready, &tab_part_o, &tab_l, "", &tab_l_ready, &tab_part_l, "o_orderkey = l_orderkey", "c1=l_orderkey,c2=o_rowid,c3=l_rowid", &tab_c, &tab_c_ready_promise, gqe::INNER_JOIN, smanual); // get future from promise std::future<size_t> tab_c_ready; tab_c_ready = tab_c_ready_promise.get_future(); // wait for future tab_c_ready.get(); // release GQE wksp.release();