From ddb64fe8266cc402d38fc3e504d12f77725800fc Mon Sep 17 00:00:00 2001 From: OpenCode Agent Date: Wed, 18 Feb 2026 02:20:29 +0000 Subject: [PATCH 1/6] MolToSmiles: skip expensive fragment splitting for single-fragment molecules For the common case of single-fragment molecules, avoid the full getMolFrags machinery (which copies and splits) and instead make a single RWMol copy. Also extract fragment processing into a shared helper and use ostringstream for SMILES construction. --- Code/GraphMol/SmilesParse/SmilesWrite.cpp | 284 +++++++++++----------- 1 file changed, 139 insertions(+), 145 deletions(-) diff --git a/Code/GraphMol/SmilesParse/SmilesWrite.cpp b/Code/GraphMol/SmilesParse/SmilesWrite.cpp index 48c714a9a5c..5a83fe82b27 100644 --- a/Code/GraphMol/SmilesParse/SmilesWrite.cpp +++ b/Code/GraphMol/SmilesParse/SmilesWrite.cpp @@ -388,7 +388,7 @@ std::string FragmentSmilesConstruct( Canon::MolStack molStack; // try to prevent excessive reallocation molStack.reserve(mol.getNumAtoms() + mol.getNumBonds()); - std::stringstream res; + std::ostringstream res; std::map ringClosureMap; int ringIdx, closureVal; @@ -490,6 +490,118 @@ static bool SortBasedOnFirstElement( namespace SmilesWrite { namespace detail { +namespace { + +void prepareMolFragment(ROMol &tmol, const SmilesWriteParams ¶ms, + bool doingCXSmiles, bool includeStereoGroups) { + for (auto atom : tmol.atoms()) { + atom->updatePropertyCache(false); + } + + if (params.doIsomericSmiles) { + tmol.setProp(common_properties::_doIsoSmiles, 1); + if (!tmol.hasProp(common_properties::_StereochemDone)) { + MolOps::assignStereochemistry(tmol, params.cleanStereo); + } + } + if (!doingCXSmiles || !includeStereoGroups) { + std::vector noStereoGroups; + tmol.setStereoGroups(noStereoGroups); + } + if (!doingCXSmiles) { + for (auto bond : tmol.bonds()) { + if (bond->getBondDir() == Bond::BondDir::UNKNOWN || + bond->getBondDir() == Bond::BondDir::EITHERDOUBLE) { + bond->setBondDir(Bond::BondDir::NONE); + } + if (bond->getStereo() == Bond::BondStereo::STEREOANY) { + bond->setStereo(Bond::BondStereo::STEREONONE); + } + } + } + if (doingCXSmiles || !params.includeDativeBonds) { + for (auto bond : tmol.bonds()) { + if (bond->getBondType() == Bond::DATIVE) { + bond->setBondType(Bond::SINGLE); + bond->getBeginAtom()->calcExplicitValence(false); + } + } + } + if (doingCXSmiles) { + for (auto bond : tmol.bonds()) { + if (bond->getBondType() == Bond::HYDROGEN) { + bond->setBondType(Bond::SINGLE); + } + } + } +} + +std::string processFragment( + ROMol &tmol, const SmilesWriteParams ¶ms, bool doingCXSmiles, + bool includeStereoGroups, int rootedAtAtom, + std::vector &atomOrdering, + std::vector &bondOrdering) { + std::vector atomMapNums(tmol.getNumAtoms(), 0); + if (params.ignoreAtomMapNumbers) { + for (auto atom : tmol.atoms()) { + atomMapNums[atom->getIdx()] = atom->getAtomMapNum(); + atom->setAtomMapNum(0); + } + } + + prepareMolFragment(tmol, params, doingCXSmiles, includeStereoGroups); + + if (params.doRandom && rootedAtAtom == -1) { + rootedAtAtom = getRandomGenerator()() % tmol.getNumAtoms(); + } + + unsigned int nAtoms = tmol.getNumAtoms(); + std::vector ranks(nAtoms); + + if (params.canonical) { + const bool breakTies = true; + const bool includeChiralPresence = false; + const bool includeIsotopes = params.doIsomericSmiles; + const bool includeChirality = params.doIsomericSmiles; + const bool inclStereoGroups = params.doIsomericSmiles; + const bool useNonStereoRanks = false; + const bool includeAtomMaps = true; + + Canon::rankMolAtoms(tmol, ranks, breakTies, includeChirality, + includeIsotopes, includeAtomMaps, + includeChiralPresence, inclStereoGroups, + useNonStereoRanks); + if (params.ignoreAtomMapNumbers) { + for (auto atom : tmol.atoms()) { + atom->setAtomMapNum(atomMapNums[atom->getIdx()]); + } + } + } else { + std::iota(ranks.begin(), ranks.end(), 0); + } + + std::vector colors(nAtoms, Canon::WHITE_NODE); + int nextAtomIdx = -1; + + if (rootedAtAtom >= 0) { + nextAtomIdx = rootedAtAtom; + } else { + unsigned int nextRank = nAtoms + 1; + for (unsigned int i = 0; i < nAtoms; i++) { + if (colors[i] == Canon::WHITE_NODE && ranks[i] < nextRank) { + nextRank = ranks[i]; + nextAtomIdx = i; + } + } + } + CHECK_INVARIANT(nextAtomIdx >= 0, "no start atom found"); + return SmilesWrite::FragmentSmilesConstruct(tmol, nextAtomIdx, colors, ranks, + params, atomOrdering, + bondOrdering); +} + +} // namespace + std::string MolToSmiles(const ROMol &mol, const SmilesWriteParams ¶ms, bool doingCXSmiles, bool includeStereoGroups) { if (!mol.getNumAtoms()) { @@ -500,6 +612,27 @@ std::string MolToSmiles(const ROMol &mol, const SmilesWriteParams ¶ms, static_cast(params.rootedAtAtom) < mol.getNumAtoms(), "rootedAtAtom must be less than the number of atoms"); + // Fast path: check fragment count to avoid expensive getMolFrags copy + std::vector fragMapping; + unsigned int numFrags = MolOps::getMolFrags(mol, fragMapping); + + if (numFrags == 1) { + // Single fragment: use a single RWMol copy instead of going through + // the full fragment-splitting machinery + RWMol tmol(mol); + int rootedAtAtom = params.rootedAtAtom; + + std::vector atomOrdering; + std::vector bondOrdering; + std::string result = processFragment(tmol, params, doingCXSmiles, + includeStereoGroups, rootedAtAtom, + atomOrdering, bondOrdering); + mol.setProp(common_properties::_smilesAtomOutputOrder, atomOrdering, true); + mol.setProp(common_properties::_smilesBondOutputOrder, bondOrdering, true); + return result; + } + + // Multi-fragment path (original logic) int rootedAtAtom; std::vector fragsRootedAtAtom; std::vector> fragsMolAtomMapping; @@ -534,164 +667,25 @@ std::string MolToSmiles(const ROMol &mol, const SmilesWriteParams ¶ms, std::vector vfragsmi(mols.size()); - // for(unsigned i=0; i> allAtomOrdering; std::vector> allBondOrdering; for (unsigned fragIdx = 0; fragIdx < mols.size(); fragIdx++) { ROMol *tmol = mols[fragIdx].get(); - // update property cache - std::vector atomMapNums(tmol->getNumAtoms(), 0); - for (auto atom : tmol->atoms()) { - if (params.ignoreAtomMapNumbers) { - atomMapNums[atom->getIdx()] = atom->getAtomMapNum(); - atom->setAtomMapNum(0); - } - atom->updatePropertyCache(false); - } - - // clean up the chirality on any atom that is marked as chiral, - // but that should not be: - if (params.doIsomericSmiles) { - tmol->setProp(common_properties::_doIsoSmiles, 1); - - if (!tmol->hasProp(common_properties::_StereochemDone)) { - MolOps::assignStereochemistry(*tmol, params.cleanStereo); - } - } - if (!doingCXSmiles || !includeStereoGroups) { - // remove any stereo groups that may be present. Otherwise they will be - // used in the canonicalization - std::vector noStereoGroups; - tmol->setStereoGroups(noStereoGroups); - } - if (!doingCXSmiles) { - // remove any wiggle bonds, unspecified double bond stereochemistry, or - // dative bonds (if we aren't doing dative bonds in the standard SMILES) - for (auto bond : tmol->bonds()) { - if (bond->getBondDir() == Bond::BondDir::UNKNOWN || - bond->getBondDir() == Bond::BondDir::EITHERDOUBLE) { - bond->setBondDir(Bond::BondDir::NONE); - } - if (bond->getStereo() == Bond::BondStereo::STEREOANY) { - bond->setStereo(Bond::BondStereo::STEREONONE); - } - } - // if other CXSMILES features are added to the canonicalization code - // in the future, they should be removed here. - } - - if (doingCXSmiles || !params.includeDativeBonds) { - // do not output dative bonds in the SMILES if we are doing CXSmiles (we - // output coordinate bonds there) or if the flag is set to ignore them - for (auto bond : tmol->bonds()) { - if (bond->getBondType() == Bond::DATIVE) { - // we are intentionally only handling DATIVE here. The other weird - // RDKit dative alternatives really shouldn't ever show up. - bond->setBondType(Bond::SINGLE); - // update the explicit valence of the begin atom since the implicit - // valence will no longer be properly perceived - bond->getBeginAtom()->calcExplicitValence(false); - } - } - } - - // if we are doing CXSMILES, Hydrogen bonds are shown as single bonds - // in the smiles part, and are indicated with the H: block of the CX - // extensions - - if (doingCXSmiles) { - for (auto bond : tmol->bonds()) { - if (bond->getBondType() == Bond::HYDROGEN) { - bond->setBondType(Bond::SINGLE); - } - } - } - rootedAtAtom = fragsRootedAtAtom[fragIdx]; - - if (params.doRandom && rootedAtAtom == -1) { - // need to find a random atom id between 0 and mol.getNumAtoms() - // exclusively - rootedAtAtom = getRandomGenerator()() % tmol->getNumAtoms(); - } - - std::string res; - unsigned int nAtoms = tmol->getNumAtoms(); - std::vector ranks(nAtoms); std::vector atomOrdering; std::vector bondOrdering; - if (params.canonical) { - const bool breakTies = true; - const bool includeChiralPresence = false; - const bool includeIsotopes = params.doIsomericSmiles; - ; - const bool includeChirality = params.doIsomericSmiles; - ; - const bool includeStereoGroups = params.doIsomericSmiles; - ; - const bool useNonStereoRanks = false; - const bool includeAtomMaps = true; - - Canon::rankMolAtoms(*tmol, ranks, breakTies, includeChirality, - includeIsotopes, includeAtomMaps, - includeChiralPresence, includeStereoGroups, - useNonStereoRanks); - if (params.ignoreAtomMapNumbers) { - for (auto atom : tmol->atoms()) { - atom->setAtomMapNum(atomMapNums[atom->getIdx()]); - } - } - } else { - std::iota(ranks.begin(), ranks.end(), 0); - } -#ifdef VERBOSE_CANON - for (unsigned int tmpI = 0; tmpI < ranks.size(); tmpI++) { - std::cout << tmpI << " " << ranks[tmpI] << " " - << *(tmol->getAtomWithIdx(tmpI)) << std::endl; - } -#endif - - std::vector colors(nAtoms, Canon::WHITE_NODE); - int nextAtomIdx = -1; - std::string subSmi; - - // find the next atom for a traverse - if (rootedAtAtom >= 0) { - nextAtomIdx = rootedAtAtom; - rootedAtAtom = -1; - } else { - unsigned int nextRank = nAtoms + 1; - for (unsigned int i = 0; i < nAtoms; i++) { - if (colors[i] == Canon::WHITE_NODE && ranks[i] < nextRank) { - nextRank = ranks[i]; - nextAtomIdx = i; - } - } - } - CHECK_INVARIANT(nextAtomIdx >= 0, "no start atom found"); - subSmi = SmilesWrite::FragmentSmilesConstruct( - *tmol, nextAtomIdx, colors, ranks, params, atomOrdering, bondOrdering); - - res += subSmi; - vfragsmi[fragIdx] = res; + vfragsmi[fragIdx] = processFragment( + *tmol, params, doingCXSmiles, includeStereoGroups, rootedAtAtom, + atomOrdering, bondOrdering); for (unsigned int &vit : atomOrdering) { - vit = fragsMolAtomMapping[fragIdx][vit]; // Lookup the Id in the - // original molecule + vit = fragsMolAtomMapping[fragIdx][vit]; } allAtomOrdering.push_back(atomOrdering); for (unsigned int &vit : bondOrdering) { - vit = fragsMolBondMapping[fragIdx][vit]; // Lookup the Id in the - // original molecule + vit = fragsMolBondMapping[fragIdx][vit]; } allBondOrdering.push_back(bondOrdering); } From 83671b6c155d3dc73aae8b573a773838910c362f Mon Sep 17 00:00:00 2001 From: "bddap (opencode opus-4.6)" Date: Sat, 28 Feb 2026 01:43:37 +0000 Subject: [PATCH 2/6] =?UTF-8?q?bench:=20opt-smiles-write=20vs=20master=20(?= =?UTF-8?q?3=C3=97100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit == /workdir/tmp/bench_results/master.txt → /workdir/tmp/bench_results/opt-smiles-write.txt == Benchmark Old (mean±sd) New (mean±sd) Δ% Speed Sig(p) ------------------------------------------------------------------------------------------------------------------------------- bench_common::nth_random 0.548 ns ± 0.00752 ns 0.548 ns ± 0.00368 ns -0.1% 1.00× faster ns p=0.944 Chirality::findPotentialStereo 587 us ± 34.9 us 597 us ± 36.7 us +1.6% 1.02× slower ns p=0.741 CIPLabeler::assignCIPLabels 266 ms ± 816 us 264 ms ± 929 us -0.7% 1.01× faster below p=0.00551 Descriptors::calcNumBridgeheadAtoms 1.03 us ± 25.9 ns 1.05 us ± 3.36 ns +1.9% 1.02× slower ns p=0.188 Descriptors::calcNumSpiroAtoms 1.22 us ± 21.1 ns 1.25 us ± 8.36 ns +2.5% 1.03× slower sig p=0.0179 InchiToInchiKey 45.7 us ± 6.16 us 35.6 us ± 4.58 us -22.0% 1.28× faster sig p=0.0235 InchiToMol 4.24 ms ± 96 us 4.18 ms ± 42.4 us -1.3% 1.01× faster ns p=0.359 memory pressure test 10 us ± 1.35 us 11.1 us ± 412 ns +11.1% 1.11× slower ns p=0.174 MolOps::addHs 419 us ± 1.16 us 419 us ± 7.86 us -0.1% 1.00× faster ns p=0.932 MolOps::assignStereochemistry legacy=false 734 us ± 25 us 760 us ± 22.4 us +3.5% 1.04× slower ns p=0.184 MolOps::assignStereochemistry legacy=true 474 us ± 8.14 us 486 us ± 16.9 us +2.5% 1.02× slower ns p=0.277 MolOps::FindSSR 192 us ± 22.1 us 174 us ± 7.4 us -9.4% 1.10× faster ns p=0.179 MolOps::getMolFrags 1.07 ms ± 12.8 us 1.09 ms ± 43 us +2.1% 1.02× slower ns p=0.381 MolPickler::molFromPickle 173 us ± 8.94 us 168 us ± 11.8 us -2.6% 1.03× faster ns p=0.602 MolPickler::pickleMol 108 us ± 6.56 us 107 us ± 7.3 us -1.1% 1.01× faster ns p=0.833 MolToCXSmiles 1.28 ms ± 35.4 us 1.23 ms ± 38.6 us -4.0% 1.04× faster ns p=0.0905 MolToInchi 2.06 ms ± 31.3 us 2.05 ms ± 46.8 us -0.2% 1.00× faster ns p=0.896 MolToSmiles 963 us ± 36.9 us 964 us ± 48.5 us +0.1% 1.00× slower ns p=0.979 MorganFingerprints::getFingerprint 185 us ± 628 ns 198 us ± 10.4 us +6.6% 1.07× slower sig p=0.0423 ROMol copy constructor 43.6 us ± 592 ns 42.5 us ± 841 ns -2.5% 1.03× faster ns p=0.0717 ROMol destructor 17.3 us ± 92.1 ns 17.5 us ± 264 ns +1.1% 1.01× slower ns p=0.241 ROMol::getNumHeavyAtoms 107 ns ± 4.37 ns 106 ns ± 1.95 ns -1.6% 1.02× faster ns p=0.528 ROMol::GetSubstructMatch 32.5 us ± 417 ns 32.5 us ± 419 ns +0.2% 1.00× slower ns p=0.863 ROMol::GetSubstructMatch patty 1.14 ms ± 39.2 us 1.13 ms ± 21.5 us -0.8% 1.01× faster ns p=0.739 ROMol::GetSubstructMatch RLewis 7 ms ± 12.5 us 7.04 ms ± 71.4 us +0.6% 1.01× slower ns p=0.333 SmilesToMol 1.25 ms ± 43.3 us 1.2 ms ± 23.8 us -3.9% 1.04× faster ns p=0.0893 Summary: sig=3, below=1 (stat-sig but < threshold), ns=22, missing=0, new_only=0 From 49e740c686fe3c6db9320be2a8bb869fe7096d80 Mon Sep 17 00:00:00 2001 From: "bddap (opencode opus-4.6)" Date: Thu, 26 Mar 2026 15:20:52 +0000 Subject: [PATCH 3/6] =?UTF-8?q?bench:=20opt-smiles-write=20vs=20master=20(?= =?UTF-8?q?5=C3=97100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit == /home/ubuntu/rdkits/tmp/bench_results/master.txt → /home/ubuntu/rdkits/tmp/bench_results/opt-smiles-write.txt == Benchmark Old (mean±sd) New (mean±sd) Δ% Speed Sig(p) --------------------------------------------------------------------------------------------------------------------- bench_common::nth_random 1.67 ns ± 0.566 ns 1.6 ns ± 0.492 ns -3.8% 1.04× faster ns p=0.851 Chirality::findPotentialStereo 1.76 ms ± 315 us 1.71 ms ± 307 us -2.9% 1.03× faster ns p=0.793 CIPLabeler::assignCIPLabels 585 ms ± 9.24 ms 587 ms ± 7.72 ms +0.2% 1.00× slower ns p=0.792 Descriptors::calcNumBridgeheadAtoms 4.7 us ± 1.6 us 6.73 us ± 1.62 us +43.3% 1.43× slower sig p=0.0454 Descriptors::calcNumSpiroAtoms 6.3 us ± 2.35 us 5.49 us ± 1.9 us -12.9% 1.15× faster ns p=0.545 InchiToInchiKey 64.8 us ± 21.6 us 57.6 us ± 16.5 us -11.0% 1.12× faster ns p=0.556 InchiToMol 11.7 ms ± 493 us 11.7 ms ± 515 us -0.3% 1.00× faster ns p=0.915 memory pressure test 23.2 us ± 7.1 us 25.2 us ± 7.6 us +8.5% 1.08× slower ns p=0.672 MolOps::addHs 923 us ± 111 us 992 us ± 25.4 us +7.5% 1.07× slower ns p=0.176 MolOps::assignStereochemistry legacy=false 2.1 ms ± 266 us 2.34 ms ± 325 us +11.4% 1.11× slower ns p=0.202 MolOps::assignStereochemistry legacy=true 1.14 ms ± 153 us 1.18 ms ± 206 us +3.4% 1.03× slower ns p=0.732 MolOps::FindSSR 418 us ± 68.5 us 386 us ± 59.3 us -7.6% 1.08× faster ns p=0.43 MolOps::getMolFrags 2.64 ms ± 377 us 2.53 ms ± 301 us -3.9% 1.04× faster ns p=0.635 MolPickler::molFromPickle 329 us ± 67.4 us 346 us ± 62.7 us +5.0% 1.05× slower ns p=0.689 MolPickler::pickleMol 280 us ± 55.3 us 309 us ± 68.3 us +10.6% 1.11× slower ns p=0.453 MolToCXSmiles 2.79 ms ± 247 us 2.59 ms ± 234 us -7.3% 1.08× faster ns p=0.18 MolToInchi 6.27 ms ± 599 us 6.5 ms ± 506 us +3.7% 1.04× slower ns p=0.512 MolToSmiles 2.16 ms ± 230 us 2.16 ms ± 229 us +0.3% 1.00× slower ns p=0.968 MorganFingerprints::getFingerprint 778 us ± 122 us 744 us ± 120 us -4.4% 1.05× faster ns p=0.657 ROMol copy constructor 220 us ± 19.7 us 202 us ± 35.3 us -8.1% 1.09× faster ns p=0.321 ROMol destructor 79.3 us ± 19.7 us 74.1 us ± 17.8 us -6.5% 1.07× faster ns p=0.662 ROMol::getNumHeavyAtoms 374 ns ± 69.4 ns 394 ns ± 72 ns +5.3% 1.05× slower ns p=0.658 ROMol::GetSubstructMatch 138 us ± 33 us 130 us ± 34.3 us -5.8% 1.06× faster ns p=0.709 ROMol::GetSubstructMatch patty 3.67 ms ± 576 us 3.59 ms ± 430 us -2.2% 1.02× faster ns p=0.8 ROMol::GetSubstructMatch RLewis 24.6 ms ± 1.25 ms 23.5 ms ± 584 us -4.5% 1.05× faster ns p=0.069 SmilesToMol 3.75 ms ± 176 us 3.31 ms ± 387 us -11.6% 1.13× faster sig p=0.0223 Summary: sig=2, below=0 (stat-sig but < threshold), ns=24, missing=0, new_only=0 From baa02b1753ee8a1ce2cd9859ab4da66fa976666f Mon Sep 17 00:00:00 2001 From: "bddap (opencode opus-4.6)" Date: Fri, 27 Mar 2026 01:26:16 +0000 Subject: [PATCH 4/6] =?UTF-8?q?bench:=20opt-smiles-write=20vs=20master=20(?= =?UTF-8?q?5=C3=97100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit == /home/ubuntu/rdkits/tmp/bench_results/master.txt → /home/ubuntu/rdkits/tmp/bench_results/opt-smiles-write.txt == Benchmark Old (mean±sd) New (mean±sd) Δ% CV Speed Sig(p) ------------------------------------------------------------------------------------------------------------------------------ bench_common::nth_random 1.23 ns ± 0.00196 ns 1.43 ns ± 0.444 ns +16.2% 31.0% 1.16× slower ns p=0.371 Chirality::findPotentialStereo 1.58 ms ± 92.9 us 1.54 ms ± 104 us -2.3% 6.8% 1.02× faster ns p=0.578 CIPLabeler::assignCIPLabels 525 ms ± 4.71 ms 523 ms ± 1.29 ms -0.5% 0.9% 1.00× faster ns p=0.312 Descriptors::calcNumBridgeheadAtoms 4.05 us ± 248 ns 4.45 us ± 1.08 us +9.8% 24.3% 1.10× slower ns p=0.465 Descriptors::calcNumSpiroAtoms 4.57 us ± 205 ns 4.84 us ± 874 ns +6.0% 18.1% 1.06× slower ns p=0.532 InchiToInchiKey 63 us ± 19.3 us 64.9 us ± 16.1 us +3.2% 30.6% 1.03× slower ns p=0.864 InchiToMol 10.1 ms ± 290 us 10.5 ms ± 253 us +4.1% 2.9% 1.04× slower ns p=0.0447 memory pressure test 21.2 us ± 3.52 us 23.5 us ± 7.19 us +11.2% 30.6% 1.11× slower ns p=0.535 MolOps::addHs 791 us ± 53.5 us 776 us ± 41.5 us -1.8% 6.8% 1.02× faster ns p=0.652 MolOps::assignStereochemistry legacy=false 1.89 ms ± 121 us 1.97 ms ± 127 us +4.2% 6.4% 1.04× slower ns p=0.341 MolOps::assignStereochemistry legacy=true 1.11 ms ± 85.4 us 1.12 ms ± 65.8 us +1.6% 7.7% 1.02× slower ns p=0.73 MolOps::FindSSR 402 us ± 30.6 us 396 us ± 20.2 us -1.4% 7.6% 1.01× faster ns p=0.739 MolOps::getMolFrags 2.29 ms ± 69 us 2.24 ms ± 121 us -2.2% 5.4% 1.02× faster ns p=0.454 MolPickler::molFromPickle 292 us ± 17.4 us 324 us ± 23.7 us +10.8% 7.3% 1.11× slower ns p=0.0461 MolPickler::pickleMol 270 us ± 19.1 us 314 us ± 37.8 us +16.3% 12.1% 1.16× slower ns p=0.0604 MolToCXSmiles 2.47 ms ± 113 us 2.44 ms ± 126 us -1.2% 5.2% 1.01× faster ns p=0.706 MolToInchi 5.76 ms ± 107 us 5.82 ms ± 209 us +1.0% 3.6% 1.01× slower ns p=0.62 MolToSmiles 1.93 ms ± 105 us 1.87 ms ± 118 us -2.8% 6.3% 1.03× faster ns p=0.463 MorganFingerprints::getFingerprint 659 us ± 68.3 us 646 us ± 32.5 us -1.9% 10.4% 1.02× faster ns p=0.722 ROMol copy constructor 151 us ± 12.7 us 155 us ± 16.5 us +2.8% 10.6% 1.03× slower ns p=0.659 ROMol destructor 61.5 us ± 7.67 us 64.1 us ± 8.89 us +4.1% 13.9% 1.04× slower ns p=0.641 ROMol::getNumHeavyAtoms 341 ns ± 39.1 ns 332 ns ± 24.8 ns -2.8% 11.5% 1.03× faster ns p=0.664 ROMol::GetSubstructMatch 111 us ± 22.6 us 115 us ± 21 us +3.6% 20.4% 1.04× slower ns p=0.778 ROMol::GetSubstructMatch patty 3.37 ms ± 195 us 3.41 ms ± 376 us +1.0% 11.0% 1.01× slower ns p=0.864 ROMol::GetSubstructMatch RLewis 21.1 ms ± 296 us 21.1 ms ± 425 us +0.2% 2.0% 1.00× slower ns p=0.879 SmilesToMol 2.92 ms ± 178 us 2.96 ms ± 185 us +1.3% 6.2% 1.01× slower ns p=0.752 Summary: sig=0, below=0 (stat-sig but < threshold), ns=26, missing=0, new_only=0 Bonferroni-corrected α = 0.001923 (26 tests, family α = 0.05) From 4a096a26aa38518a62517ac736d56090eaece9cd Mon Sep 17 00:00:00 2001 From: "bddap (opencode opus-4.6)" Date: Fri, 27 Mar 2026 20:02:20 +0000 Subject: [PATCH 5/6] =?UTF-8?q?bench:=20opt-smiles-write=20vs=20master=20(?= =?UTF-8?q?5=C3=97100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit == /home/ubuntu/rdkits/tmp/bench_results/master.txt → /home/ubuntu/rdkits/tmp/bench_results/opt-smiles-write.txt == Benchmark Old (mean±sd) New (mean±sd) Δ% CV Speed Sig(p) --------------------------------------------------------------------------------------------------------------------------- bench_common::nth_random 1.6 ns ± 0.534 ns 1.56 ns ± 0.509 ns -2.5% 33.4% 1.03× faster ns p=0.907 Chirality::findPotentialStereo 1.66 ms ± 137 us 1.61 ms ± 97.8 us -2.9% 8.2% 1.03× faster ns p=0.541 CIPLabeler::assignCIPLabels 577 ms ± 49.4 ms 573 ms ± 37.3 ms -0.7% 8.6% 1.01× faster ns p=0.891 Descriptors::calcNumBridgeheadAtoms 5.02 us ± 1.14 us 4.52 us ± 1.36 us -10.0% 30.1% 1.11× faster ns p=0.545 Descriptors::calcNumSpiroAtoms 5.19 us ± 1 us 4.66 us ± 383 ns -10.2% 19.3% 1.11× faster ns p=0.319 InchiToInchiKey 52.1 us ± 3.78 us 58.5 us ± 14.7 us +12.2% 25.1% 1.12× slower ns p=0.395 InchiToMol 10.5 ms ± 207 us 10.6 ms ± 202 us +0.2% 2.0% 1.00× slower ns p=0.874 memory pressure test 17.3 us ± 2.9 us 15.5 us ± 3.1 us -10.1% 20.0% 1.11× faster ns p=0.387 MolOps::addHs 826 us ± 36.9 us 835 us ± 60 us +1.1% 7.2% 1.01× slower ns p=0.785 MolOps::assignStereochemistry legacy=false 1.91 ms ± 125 us 2 ms ± 115 us +4.4% 6.5% 1.04× slower ns p=0.302 MolOps::assignStereochemistry legacy=true 1.12 ms ± 37.8 us 1.2 ms ± 68.4 us +7.5% 5.7% 1.08× slower ns p=0.0517 MolOps::FindSSR 371 us ± 18.5 us 396 us ± 26.4 us +6.8% 6.7% 1.07× slower ns p=0.125 MolOps::getMolFrags 2.36 ms ± 185 us 2.32 ms ± 137 us -1.7% 7.8% 1.02× faster ns p=0.702 MolPickler::molFromPickle 329 us ± 38.1 us 343 us ± 31.5 us +4.2% 11.6% 1.04× slower ns p=0.552 MolPickler::pickleMol 272 us ± 23.9 us 291 us ± 33.9 us +6.9% 11.7% 1.07× slower ns p=0.342 MolToCXSmiles 2.58 ms ± 167 us 2.53 ms ± 178 us -1.9% 7.0% 1.02× faster ns p=0.661 MolToInchi 6.12 ms ± 324 us 6.29 ms ± 226 us +2.7% 5.3% 1.03× slower ns p=0.375 MolToSmiles 2.02 ms ± 124 us 1.96 ms ± 36 us -3.0% 6.2% 1.03× faster ns p=0.345 MorganFingerprints::getFingerprint 662 us ± 54.1 us 658 us ± 49.4 us -0.6% 8.2% 1.01× faster ns p=0.91 ROMol copy constructor 157 us ± 15.5 us 166 us ± 19.2 us +5.7% 11.6% 1.06× slower ns p=0.439 ROMol destructor 61 us ± 4.92 us 66.5 us ± 9.66 us +9.1% 14.5% 1.09× slower ns p=0.297 ROMol::getNumHeavyAtoms 359 ns ± 59.4 ns 328 ns ± 6.93 ns -8.9% 16.5% 1.10× faster ns p=0.296 ROMol::GetSubstructMatch 116 us ± 15 us 125 us ± 16.5 us +7.7% 13.2% 1.08× slower ns p=0.395 ROMol::GetSubstructMatch patty 3.6 ms ± 147 us 3.59 ms ± 162 us -0.3% 4.5% 1.00× faster ns p=0.912 ROMol::GetSubstructMatch RLewis 22 ms ± 593 us 22.4 ms ± 846 us +1.8% 3.8% 1.02× slower ns p=0.431 SmilesToMol 3.09 ms ± 201 us 2.94 ms ± 192 us -4.8% 6.5% 1.05× faster ns p=0.266 Summary: sig=0, below=0 (stat-sig but < threshold), ns=26, missing=0, new_only=0 Bonferroni-corrected α = 0.001923 (26 tests, family α = 0.05) From 2c27da491d57364a42b96663d50bc54094c8f7ef Mon Sep 17 00:00:00 2001 From: "bddap (opencode opus-4.6)" Date: Sat, 28 Mar 2026 14:45:34 +0000 Subject: [PATCH 6/6] =?UTF-8?q?bench:=20opt-smiles-write=20vs=20master=20(?= =?UTF-8?q?16=C3=97800)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit == /home/ubuntu/rdkits/tmp/bench_results/master.txt → /home/ubuntu/rdkits/tmp/bench_results/opt-smiles-write.txt == Benchmark Old (mean±sd) New (mean±sd) Δ% CV Speed Sig(p) --------------------------------------------------------------------------------------------------------------------------- bench_common::nth_random 1.37 ns ± 0.14 ns 1.42 ns ± 0.144 ns +3.6% 10.2% 1.04× slower ns p=0.33 Chirality::findPotentialStereo 1.6 ms ± 54.1 us 1.57 ms ± 62.5 us -1.8% 4.0% 1.02× faster ns p=0.174 CIPLabeler::assignCIPLabels 580 ms ± 34.8 ms 567 ms ± 40.3 ms -2.2% 7.1% 1.02× faster ns p=0.349 Descriptors::calcNumBridgeheadAtoms 4.45 us ± 519 ns 4.37 us ± 463 ns -1.8% 11.7% 1.02× faster ns p=0.649 Descriptors::calcNumSpiroAtoms 4.81 us ± 442 ns 4.97 us ± 526 ns +3.3% 10.6% 1.03× slower ns p=0.357 InchiToInchiKey 50.3 us ± 5.2 us 49.3 us ± 4.54 us -1.9% 10.3% 1.02× faster ns p=0.574 InchiToMol 10.5 ms ± 247 us 10.5 ms ± 256 us -0.7% 2.4% 1.01× faster ns p=0.386 memory pressure test 18 us ± 3.89 us 16.5 us ± 3.48 us -8.0% 21.6% 1.09× faster ns p=0.277 MolOps::addHs 783 us ± 42 us 777 us ± 55.1 us -0.7% 7.1% 1.01× faster ns p=0.742 MolOps::assignStereochemistry legacy=false 1.93 ms ± 90.7 us 1.92 ms ± 68.1 us -0.4% 4.7% 1.00× faster ns p=0.813 MolOps::assignStereochemistry legacy=true 1.12 ms ± 39.1 us 1.11 ms ± 43.6 us -0.4% 3.9% 1.00× faster ns p=0.75 MolOps::FindSSR 372 us ± 19 us 371 us ± 22.7 us -0.3% 6.1% 1.00× faster ns p=0.899 MolOps::getMolFrags 2.3 ms ± 123 us 2.26 ms ± 89.4 us -1.6% 5.3% 1.02× faster ns p=0.343 MolPickler::molFromPickle 295 us ± 18.7 us 299 us ± 20.7 us +1.5% 6.9% 1.02× slower ns p=0.519 MolPickler::pickleMol 286 us ± 20.3 us 279 us ± 17.3 us -2.3% 7.1% 1.02× faster ns p=0.337 MolToCXSmiles 2.54 ms ± 101 us 2.5 ms ± 96 us -1.6% 4.0% 1.02× faster ns p=0.25 MolToInchi 6.06 ms ± 155 us 6.04 ms ± 154 us -0.3% 2.6% 1.00× faster ns p=0.762 MolToSmiles 2 ms ± 94.8 us 1.93 ms ± 108 us -3.6% 5.6% 1.04× faster ns p=0.0528 MorganFingerprints::getFingerprint 630 us ± 27.7 us 606 us ± 20.9 us -3.9% 4.4% 1.04× faster ns p=0.00843 ROMol copy constructor 148 us ± 8.41 us 151 us ± 10.9 us +1.9% 7.2% 1.02× slower ns p=0.431 ROMol destructor 62.7 us ± 3.14 us 64.3 us ± 4.04 us +2.5% 6.3% 1.03× slower ns p=0.224 ROMol::getNumHeavyAtoms 347 ns ± 26.1 ns 350 ns ± 26.5 ns +0.8% 7.6% 1.01× slower ns p=0.766 ROMol::GetSubstructMatch 113 us ± 8.13 us 117 us ± 7.77 us +3.1% 7.2% 1.03× slower ns p=0.226 ROMol::GetSubstructMatch patty 3.63 ms ± 88.2 us 3.64 ms ± 54.2 us +0.2% 2.4% 1.00× slower ns p=0.747 ROMol::GetSubstructMatch RLewis 22 ms ± 513 us 22.1 ms ± 441 us +0.2% 2.3% 1.00× slower ns p=0.763 SmilesToMol 2.98 ms ± 89.1 us 2.95 ms ± 106 us -1.1% 3.6% 1.01× faster ns p=0.369 Summary: sig=0, below=0 (stat-sig but < threshold), ns=26, missing=0, new_only=0 Bonferroni-corrected α = 0.001923 (26 tests, family α = 0.05)