| … | … | @@ -10,6 +10,9 @@ |
| 10 | 10 | * SPDX-License-Identifier: AGPL-3.0-or-later |
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | #include <algorithm> |
| 14 | #include <set> |
| 15 | |
| 13 | 16 | #include <opencog/atoms/base/Link.h> |
| 14 | 17 | #include <opencog/atoms/base/Node.h> |
| 15 | 18 | #include <opencog/atoms/atom_types/atom_types.h> |
| … | … | @@ -317,47 +320,153 @@ void GpuStorageNode::storeValue(const Handle&, const Handle&) {} |
| 317 | 320 | void GpuStorageNode::loadValue(const Handle&, const Handle&) {} |
| 318 | 321 | |
| 319 | 322 | // ================================================================ |
| 320 | | // GPU-resident query (the runQuery override). |
| 323 | // GPU-resident query (the runQuery override) — GENERAL multi-clause. |
| 321 | 324 | // |
| 322 | | // Fast-path for (Meet <vardecl> (Inheritance $X <const>)) — a single |
| 323 | | // typed clause with one variable in position 0 and a constant in position 1. |
| 324 | | // The match is the incoming InheritanceLinks of <const> on the GPU; the |
| 325 | | // groundings are their position-0 atoms. Returns true if handled. |
| 325 | // Handles (Meet <vardecl> body) where there is exactly ONE variable and |
| 326 | // body is a single clause or an AndLink of clauses, each clause one of: |
| 327 | // (a) a direct binary link (T $X c) / (T c $X) [e.g. InheritanceLink] |
| 328 | // (b) an Eval relation (Evaluation (Predicate P) (List $X c)) / (List c $X) |
| 329 | // Each clause's candidate var-bindings are computed via the GPU incoming |
| 330 | // index; the answer is their intersection. Anything else (multi-variable |
| 331 | // joins, nesting, etc.) returns false → default BackingStore::runQuery. |
| 332 | |
| 333 | // Single query variable, or UNDEFINED if not exactly one. |
| 334 | static Handle gpu_single_var(const Handle& vardecl) |
| 335 | { |
| 336 | Type t = vardecl->get_type(); |
| 337 | if (VARIABLE_NODE == t) return vardecl; |
| 338 | if (TYPED_VARIABLE_LINK == t) return vardecl->getOutgoingAtom(0); |
| 339 | if (VARIABLE_LIST == t or VARIABLE_SET == t) |
| 340 | { |
| 341 | if (1 != vardecl->get_arity()) return Handle::UNDEFINED; |
| 342 | return gpu_single_var(vardecl->getOutgoingAtom(0)); |
| 343 | } |
| 344 | return Handle::UNDEFINED; |
| 345 | } |
| 346 | |
| 347 | bool GpuStorageNode::gpu_clause_candidates(const Handle& clause, const Handle& var, |
| 348 | std::set<uint32_t>& out) |
| 349 | { |
| 350 | Type ct = clause->get_type(); |
| 351 | |
| 352 | // --- shape (b): Eval relation (Predicate, List) --- |
| 353 | if (EVALUATION_LINK == ct and 2 == clause->get_arity() and |
| 354 | LIST_LINK == clause->getOutgoingAtom(1)->get_type()) |
| 355 | { |
| 356 | const Handle& pred = clause->getOutgoingAtom(0); |
| 357 | const Handle& lst = clause->getOutgoingAtom(1); |
| 358 | if (2 != lst->get_arity()) return false; |
| 359 | int varpos = (lst->getOutgoingAtom(0) == var) ? 0 |
| 360 | : (lst->getOutgoingAtom(1) == var) ? 1 : -1; |
| 361 | if (varpos < 0) return false; |
| 362 | const Handle& cst = lst->getOutgoingAtom(1 - varpos); |
| 363 | if (not cst->is_node() or not pred->is_node()) return false; |
| 364 | auto pit = _handle_to_slot.find(pred); if (pit == _handle_to_slot.end()) return false; |
| 365 | auto cit = _handle_to_slot.find(cst); if (cit == _handle_to_slot.end()) return false; |
| 366 | uint32_t pslot = pit->second, cslot = cit->second, cstpos = 1 - varpos; |
| 367 | |
| 368 | uint32_t n = 0; |
| 369 | gpu_fetch_incoming(&_gpu_table, cslot, nullptr, 0, &n); |
| 370 | if (0 == n) return true; |
| 371 | std::vector<uint32_t> cin(n); |
| 372 | gpu_fetch_incoming(&_gpu_table, cslot, cin.data(), n, &n); |
| 373 | for (uint32_t lslot : cin) // candidate ListLink slots |
| 374 | { |
| 375 | uint16_t lt; uint32_t lob[8]; uint16_t lar = 8; |
| 376 | if (gpu_fetch_link(&_gpu_table, lslot, <, lob, &lar) != 0) continue; |
| 377 | if ((Type)lt != LIST_LINK or lar != 2) continue; |
| 378 | if (lob[cstpos] != cslot) continue; // const in the right list position |
| 379 | uint32_t binding = lob[varpos]; |
| 380 | // require an EvaluationLink(pred, thisList) |
| 381 | uint32_t m = 0; |
| 382 | gpu_fetch_incoming(&_gpu_table, lslot, nullptr, 0, &m); |
| 383 | if (0 == m) continue; |
| 384 | std::vector<uint32_t> lin(m); |
| 385 | gpu_fetch_incoming(&_gpu_table, lslot, lin.data(), m, &m); |
| 386 | for (uint32_t eslot : lin) |
| 387 | { |
| 388 | uint16_t et; uint32_t eob[8]; uint16_t ear = 8; |
| 389 | if (gpu_fetch_link(&_gpu_table, eslot, &et, eob, &ear) != 0) continue; |
| 390 | if ((Type)et == EVALUATION_LINK and ear == 2 and |
| 391 | eob[0] == pslot and eob[1] == lslot) |
| 392 | { out.insert(binding); break; } |
| 393 | } |
| 394 | } |
| 395 | return true; |
| 396 | } |
| 397 | |
| 398 | // --- shape (a): direct binary link (T $X c) / (T c $X) --- |
| 399 | if (2 == clause->get_arity()) |
| 400 | { |
| 401 | const Handle& o0 = clause->getOutgoingAtom(0); |
| 402 | const Handle& o1 = clause->getOutgoingAtom(1); |
| 403 | int varpos = (o0 == var) ? 0 : (o1 == var) ? 1 : -1; |
| 404 | if (varpos < 0) return false; |
| 405 | const Handle& cst = (0 == varpos) ? o1 : o0; |
| 406 | if (not cst->is_node()) return false; |
| 407 | auto it = _handle_to_slot.find(cst); if (it == _handle_to_slot.end()) return false; |
| 408 | uint32_t cslot = it->second, cstpos = 1 - varpos; |
| 409 | |
| 410 | uint32_t n = 0; |
| 411 | gpu_fetch_incoming(&_gpu_table, cslot, nullptr, 0, &n); |
| 412 | if (0 == n) return true; |
| 413 | std::vector<uint32_t> cin(n); |
| 414 | gpu_fetch_incoming(&_gpu_table, cslot, cin.data(), n, &n); |
| 415 | for (uint32_t ls : cin) |
| 416 | { |
| 417 | uint16_t lt; uint32_t ob[8]; uint16_t ar = 8; |
| 418 | if (gpu_fetch_link(&_gpu_table, ls, <, ob, &ar) != 0) continue; |
| 419 | if ((Type)lt != ct or ar != 2) continue; |
| 420 | if (ob[cstpos] != cslot) continue; |
| 421 | out.insert(ob[varpos]); |
| 422 | } |
| 423 | return true; |
| 424 | } |
| 425 | |
| 426 | return false; |
| 427 | } |
| 326 | 428 | |
| 327 | 429 | bool GpuStorageNode::gpu_run_meet(const Handle& query, const Handle& key) |
| 328 | 430 | { |
| 329 | 431 | if (query->get_type() != MEET_LINK || query->get_arity() != 2) return false; |
| 432 | Handle var = gpu_single_var(query->getOutgoingAtom(0)); |
| 433 | if (nullptr == var || var->get_type() != VARIABLE_NODE) return false; // single-var only |
| 434 | |
| 330 | 435 | const Handle& body = query->getOutgoingAtom(1); |
| 331 | | if (body->get_type() != INHERITANCE_LINK || body->get_arity() != 2) return false; |
| 332 | | const Handle& var = body->getOutgoingAtom(0); |
| 333 | | const Handle& cst = body->getOutgoingAtom(1); |
| 334 | | if (var->get_type() != VARIABLE_NODE) return false; // shape: (Inheritance $X const) |
| 335 | | if (not cst->is_node()) return false; |
| 436 | HandleSeq clauses; |
| 437 | if (AND_LINK == body->get_type()) clauses = body->getOutgoingSet(); |
| 438 | else clauses.push_back(body); |
| 439 | if (clauses.empty()) return false; |
| 440 | |
| 336 | 441 | AtomSpace* as = query->getAtomSpace(); |
| 337 | 442 | if (nullptr == as) return false; |
| 338 | 443 | |
| 339 | 444 | std::lock_guard<std::mutex> lk(_mtx); |
| 340 | | auto it = _handle_to_slot.find(cst); |
| 341 | | if (it == _handle_to_slot.end()) return false; // const not on GPU → default path |
| 342 | 445 | ensure_incoming(); |
| 343 | 446 | |
| 344 | | uint32_t slot = it->second, count = 0; |
| 345 | | gpu_fetch_incoming(&_gpu_table, slot, nullptr, 0, &count); |
| 346 | | |
| 347 | | UnisetValuePtr svp(createUnisetValue()); |
| 348 | | if (count > 0) |
| 447 | std::set<uint32_t> result; |
| 448 | bool first = true; |
| 449 | for (const Handle& c : clauses) |
| 349 | 450 | { |
| 350 | | std::vector<uint32_t> links(count); |
| 351 | | gpu_fetch_incoming(&_gpu_table, slot, links.data(), count, &count); |
| 352 | | for (uint32_t ls : links) |
| 451 | std::set<uint32_t> cand; |
| 452 | if (not gpu_clause_candidates(c, var, cand)) return false; // unsupported → fallback |
| 453 | if (first) { result = std::move(cand); first = false; } |
| 454 | else |
| 353 | 455 | { |
| 354 | | uint16_t lt; uint32_t obuf[8]; uint16_t ar = 8; |
| 355 | | if (gpu_fetch_link(&_gpu_table, ls, <, obuf, &ar) != 0) continue; |
| 356 | | if ((Type)lt != INHERITANCE_LINK || ar != 2) continue; |
| 357 | | if ((uint32_t)obuf[1] != slot) continue; // const must be in position 1 |
| 358 | | Handle x = _tlb.getAtom((UUID)obuf[0]); |
| 359 | | if (x) svp->add(as->add_atom(x)); |
| 456 | std::set<uint32_t> inter; |
| 457 | std::set_intersection(result.begin(), result.end(), |
| 458 | cand.begin(), cand.end(), |
| 459 | std::inserter(inter, inter.begin())); |
| 460 | result = std::move(inter); |
| 360 | 461 | } |
| 462 | if (result.empty()) break; |
| 463 | } |
| 464 | |
| 465 | UnisetValuePtr svp(createUnisetValue()); |
| 466 | for (uint32_t s : result) |
| 467 | { |
| 468 | Handle h = _tlb.getAtom((UUID)s); |
| 469 | if (h) svp->add(as->add_atom(h)); |
| 361 | 470 | } |
| 362 | 471 | svp->close(); |
| 363 | 472 | query->setValue(key, svp); |