⚡️ Speed up function _is_metadata_of by 3,730%
#320
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📄 3,730% (37.30x) speedup for
_is_metadata_ofinpandas/io/pytables.py⏱️ Runtime :
26.3 milliseconds→687 microseconds(best of59runs)📝 Explanation and details
The optimized code achieves a 3729% speedup through two key algorithmic improvements that dramatically reduce unnecessary work:
1. Early Short-Circuit on Name Mismatch
The most impactful change moves the
current._v_name != "meta"check before the parent equality check. In the original code, when a node isn't named "meta", it still traverses the entire ancestry chain checking every parent. The optimized version immediately returnsFalsewhen encountering a non-"meta" node, eliminating thousands of unnecessary iterations.This is evident in the profiler results: the original code executed 507,074 loop iterations vs only 2,552 in the optimized version - a 99.5% reduction in loop overhead.
2. Minor Attribute Access Optimizations
_v_depthvalues upfront to avoid repeated attribute lookupscurrent = parentinstead ofcurrent = current._v_parentto eliminate one attribute access per loopPerformance Impact Analysis:
The test results show the optimization is most effective for:
test_large_scale_all_non_meta_namesshows 9828% speedup when no nodes are named "meta"test_large_deeply_nested_metashows 4345% speedup for 500-level deep treesThe optimization performs slightly slower (1-24%) only on simple positive cases where the meta node is found immediately, but these represent minimal absolute time differences (nanoseconds).
This optimization is particularly valuable in PyTables metadata scanning scenarios where most nodes are not metadata groups, allowing the function to quickly eliminate false candidates rather than exhaustively traversing deep hierarchies.
✅ Correctness verification report:
🌀 Generated Regression Tests and Runtime
from future import annotations
imports
import pytest
from pandas.io.pytables import _is_metadata_of
class Node:
"""Minimal stub of tables.Node for testing purposes."""
def init(self, name, depth, parent=None):
self._v_name = name
self._v_depth = depth
self._v_parent = parent
from pandas.io.pytables import _is_metadata_of
---------------------- UNIT TESTS ----------------------
----------- Basic Test Cases -----------
def test_basic_true_direct_meta_child():
# meta group is a direct child of parent_group
parent = Node("parent", 1)
meta = Node("meta", 2, parent)
codeflash_output = _is_metadata_of(meta, parent) # 843ns -> 863ns (2.32% slower)
def test_basic_false_wrong_name():
# group is a direct child but not named "meta"
parent = Node("parent", 1)
not_meta = Node("notmeta", 2, parent)
codeflash_output = _is_metadata_of(not_meta, parent) # 914ns -> 824ns (10.9% faster)
def test_basic_false_wrong_parent():
# meta group is not a child of parent_group
parent = Node("parent", 1)
other_parent = Node("other", 1)
meta = Node("meta", 2, other_parent)
codeflash_output = _is_metadata_of(meta, parent) # 882ns -> 967ns (8.79% slower)
def test_basic_true_nested_meta():
# meta group is nested under another group but is a child of parent_group
parent = Node("parent", 1)
intermediate = Node("intermediate", 2, parent)
meta = Node("meta", 3, intermediate)
# Should be False: meta's parent is intermediate, not parent
codeflash_output = _is_metadata_of(meta, parent) # 1.07μs -> 1.18μs (8.82% slower)
def test_basic_true_meta_grandchild():
# meta group is grandchild, but only direct children should be considered
parent = Node("parent", 1)
child = Node("child", 2, parent)
meta = Node("meta", 3, child)
# Should be False: meta's parent is child, not parent
codeflash_output = _is_metadata_of(meta, parent) # 1.16μs -> 1.12μs (3.11% faster)
----------- Edge Test Cases -----------
def test_edge_same_depth():
# group and parent_group have the same depth
parent = Node("parent", 2)
group = Node("meta", 2, parent)
codeflash_output = _is_metadata_of(group, parent) # 527ns -> 559ns (5.72% slower)
def test_edge_group_is_root():
# group is root, cannot be metadata of anything
root = Node("root", 1)
codeflash_output = _is_metadata_of(root, root) # 575ns -> 545ns (5.50% faster)
def test_edge_parent_is_root_meta_is_child():
# parent is root, meta is direct child
root = Node("root", 1)
meta = Node("meta", 2, root)
codeflash_output = _is_metadata_of(meta, root) # 878ns -> 990ns (11.3% slower)
def test_edge_meta_name_case_sensitive():
# meta name is case sensitive
parent = Node("parent", 1)
meta = Node("Meta", 2, parent)
codeflash_output = _is_metadata_of(meta, parent) # 1.01μs -> 901ns (12.5% faster)
def test_edge_meta_name_with_spaces():
# meta name with spaces
parent = Node("parent", 1)
meta = Node("meta ", 2, parent)
codeflash_output = _is_metadata_of(meta, parent) # 984ns -> 838ns (17.4% faster)
def test_edge_meta_name_empty_string():
# meta name is empty string
parent = Node("parent", 1)
meta = Node("", 2, parent)
codeflash_output = _is_metadata_of(meta, parent) # 989ns -> 813ns (21.6% faster)
def test_edge_parent_is_none():
# parent_group is None
parent = None
meta = Node("meta", 2, parent)
# Should raise AttributeError due to NoneType
with pytest.raises(AttributeError):
_is_metadata_of(meta, parent) # 1.30μs -> 1.29μs (0.309% faster)
def test_edge_group_parent_is_none():
# group._v_parent is None, simulating orphan node
parent = Node("parent", 1)
meta = Node("meta", 2, None)
# Should traverse up and fail to find parent_group
codeflash_output = _is_metadata_of(meta, parent)
def test_edge_group_depth_less_than_parent():
# group depth < parent_group depth
parent = Node("parent", 2)
group = Node("meta", 1, parent)
codeflash_output = _is_metadata_of(group, parent) # 739ns -> 686ns (7.73% faster)
def test_edge_group_depth_equal_parent():
# group depth == parent_group depth
parent = Node("parent", 2)
group = Node("meta", 2, parent)
codeflash_output = _is_metadata_of(group, parent) # 630ns -> 552ns (14.1% faster)
def test_edge_group_depth_one_parent_depth_one():
# both group and parent_group are root nodes
root1 = Node("root1", 1)
root2 = Node("root2", 1)
codeflash_output = _is_metadata_of(root1, root2) # 545ns -> 560ns (2.68% slower)
def test_edge_group_is_none():
# group is None
parent = Node("parent", 1)
group = None
with pytest.raises(AttributeError):
_is_metadata_of(group, parent) # 1.23μs -> 1.22μs (1.48% faster)
def test_edge_parent_is_self():
# group and parent_group are the same object
node = Node("meta", 2)
codeflash_output = _is_metadata_of(node, node) # 642ns -> 574ns (11.8% faster)
def test_edge_meta_is_not_direct_child_but_has_meta_name():
# meta group is not a direct child, but has meta name
parent = Node("parent", 1)
child = Node("child", 2, parent)
meta = Node("meta", 3, child)
codeflash_output = _is_metadata_of(meta, parent) # 1.34μs -> 1.26μs (6.19% faster)
def test_edge_meta_with_non_node_parent():
# meta group has a parent that is not a Node instance
parent = Node("parent", 1)
meta = Node("meta", 2, "not_a_node")
# Should traverse up and fail to find parent_group
codeflash_output = _is_metadata_of(meta, parent)
----------- Large Scale Test Cases -----------
def test_large_scale_chain_of_meta_groups():
# Create a long chain of nodes, only one is 'meta' and child of parent_group
parent = Node("parent", 1)
prev = parent
nodes = []
for i in range(2, 1001):
name = "meta" if i == 500 else f"group{i}"
node = Node(name, i, prev)
nodes.append(node)
prev = node
# Only nodes[498] is named 'meta' and its parent is nodes[497]
codeflash_output = _is_metadata_of(nodes[498], nodes[497]) # 961ns -> 1.09μs (12.0% slower)
# Test that nodes[499] (not named 'meta') is not metadata of nodes[498]
codeflash_output = _is_metadata_of(nodes[499], nodes[498]) # 26.0μs -> 541ns (4702% faster)
def test_large_scale_meta_at_various_depths():
# Create multiple meta nodes at different depths, only direct child should pass
parent = Node("parent", 1)
meta_direct = Node("meta", 2, parent)
meta_far = Node("meta", 1000, meta_direct)
# Only direct child should return True
codeflash_output = _is_metadata_of(meta_direct, parent) # 883ns -> 980ns (9.90% slower)
codeflash_output = _is_metadata_of(meta_far, parent) # 622ns -> 634ns (1.89% slower)
def test_large_scale_all_non_meta_names():
# Create 1000 nodes, none are named 'meta'
parent = Node("parent", 1)
prev = parent
for i in range(2, 1002):
node = Node(f"group{i}", i, prev)
prev = node
codeflash_output = _is_metadata_of(node, parent) # 25.5ms -> 256μs (9828% faster)
def test_large_scale_meta_with_multiple_parents():
# Simulate a tree with multiple meta nodes, only correct parent should pass
parent1 = Node("parent1", 1)
parent2 = Node("parent2", 1)
meta1 = Node("meta", 2, parent1)
meta2 = Node("meta", 2, parent2)
codeflash_output = _is_metadata_of(meta1, parent1) # 901ns -> 892ns (1.01% faster)
codeflash_output = _is_metadata_of(meta2, parent1) # 474ns -> 579ns (18.1% slower)
codeflash_output = _is_metadata_of(meta2, parent2) # 339ns -> 334ns (1.50% faster)
def test_large_scale_multiple_meta_nodes():
# Create a chain with multiple meta nodes, only correct parent should pass
parent = Node("parent", 1)
meta1 = Node("meta", 2, parent)
meta2 = Node("meta", 3, meta1)
meta3 = Node("meta", 4, meta2)
# Only meta1 is direct child of parent
codeflash_output = _is_metadata_of(meta1, parent) # 802ns -> 837ns (4.18% slower)
codeflash_output = _is_metadata_of(meta2, parent) # 619ns -> 648ns (4.48% slower)
codeflash_output = _is_metadata_of(meta3, parent) # 534ns -> 504ns (5.95% faster)
codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import pytest
from pandas.io.pytables import _is_metadata_of
Minimal mock class to simulate PyTables Node behavior
class MockNode:
def init(self, name, depth, parent=None):
self._v_name = name
self._v_depth = depth
self._v_parent = parent
from pandas.io.pytables import _is_metadata_of
---------------------------
BASIC TEST CASES
---------------------------
def test_basic_true_direct_child_meta():
# group: /root/parent/meta
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
meta = MockNode("meta", 3, parent)
# meta is a direct child of parent, named "meta"
codeflash_output = _is_metadata_of(meta, parent) # 1.36μs -> 1.37μs (0.586% slower)
def test_basic_false_not_named_meta():
# group: /root/parent/notmeta
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
notmeta = MockNode("notmeta", 3, parent)
# notmeta is a direct child of parent, but not named "meta"
codeflash_output = _is_metadata_of(notmeta, parent) # 1.50μs -> 854ns (75.5% faster)
def test_basic_false_not_child():
# group: /root/parent/meta, parent_group: /root/other
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
other = MockNode("other", 2, root)
meta = MockNode("meta", 3, parent)
# meta is not a child of other
codeflash_output = _is_metadata_of(meta, other) # 1.18μs -> 1.45μs (18.9% slower)
---------------------------
EDGE TEST CASES
---------------------------
def test_edge_group_same_as_parent():
# group and parent_group are the same node
root = MockNode("root", 1)
codeflash_output = _is_metadata_of(root, root) # 529ns -> 535ns (1.12% slower)
def test_edge_group_depth_less_than_parent():
# group is higher up than parent_group
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
codeflash_output = _is_metadata_of(root, parent) # 555ns -> 567ns (2.12% slower)
def test_edge_group_depth_equal_to_parent():
# group and parent_group at same depth
root = MockNode("root", 1)
parent1 = MockNode("parent1", 2, root)
parent2 = MockNode("parent2", 2, root)
codeflash_output = _is_metadata_of(parent1, parent2) # 543ns -> 550ns (1.27% slower)
def test_edge_group_far_descendant_meta():
# group: /root/parent/meta/child/grandchild
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
meta = MockNode("meta", 3, parent)
child = MockNode("child", 4, meta)
grandchild = MockNode("grandchild", 5, child)
# Should still return True (meta is named "meta" and is child of parent)
codeflash_output = _is_metadata_of(grandchild, parent) # 1.46μs -> 879ns (66.4% faster)
def test_edge_group_far_descendant_nonmeta():
# group: /root/parent/notmeta/child/grandchild
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
notmeta = MockNode("notmeta", 3, parent)
child = MockNode("child", 4, notmeta)
grandchild = MockNode("grandchild", 5, child)
# Should return False (no "meta" node in ancestry under parent)
codeflash_output = _is_metadata_of(grandchild, parent) # 1.59μs -> 886ns (79.6% faster)
def test_edge_multiple_meta_nodes():
# group: /root/parent/meta/child/meta/grandchild
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
meta1 = MockNode("meta", 3, parent)
child = MockNode("child", 4, meta1)
meta2 = MockNode("meta", 5, child)
grandchild = MockNode("grandchild", 6, meta2)
# Should return True (first meta under parent is enough)
codeflash_output = _is_metadata_of(grandchild, parent) # 1.39μs -> 846ns (63.7% faster)
def test_edge_meta_node_not_under_parent():
# group: /root/other/meta
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
other = MockNode("other", 2, root)
meta = MockNode("meta", 3, other)
# meta is not under parent
codeflash_output = _is_metadata_of(meta, parent) # 1.22μs -> 1.41μs (13.6% slower)
def test_edge_parent_is_root():
# group: /root/meta
root = MockNode("root", 1)
meta = MockNode("meta", 2, root)
codeflash_output = _is_metadata_of(meta, root) # 959ns -> 1.19μs (19.1% slower)
def test_edge_group_depth_one():
# group is at depth 1 (root), parent_group at depth 1 (also root)
root = MockNode("root", 1)
codeflash_output = _is_metadata_of(root, root) # 548ns -> 530ns (3.40% faster)
def test_edge_group_depth_two_parent_root():
# group: /root/child, parent_group: /root
root = MockNode("root", 1)
child = MockNode("meta", 2, root)
codeflash_output = _is_metadata_of(child, root) # 1.02μs -> 1.21μs (15.4% slower)
def test_edge_group_is_none():
# group is None (should raise AttributeError)
root = MockNode("root", 1)
with pytest.raises(AttributeError):
_is_metadata_of(None, root) # 1.22μs -> 1.21μs (0.412% faster)
def test_edge_parent_is_none():
# parent_group is None (should raise AttributeError)
root = MockNode("root", 1)
meta = MockNode("meta", 2, root)
with pytest.raises(AttributeError):
_is_metadata_of(meta, None) # 1.19μs -> 1.18μs (0.845% faster)
---------------------------
LARGE SCALE TEST CASES
---------------------------
def test_large_deeply_nested_meta():
# Build a chain: /root/p/meta/child1/child2/.../childN
N = 500 # large but within reasonable test bounds
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
meta = MockNode("meta", 3, parent)
current = meta
for i in range(4, N+4):
current = MockNode(f"child{i}", i, current)
# The deepest node should be metadata of parent
codeflash_output = _is_metadata_of(current, parent) # 42.6μs -> 958ns (4345% faster)
def test_large_many_siblings():
# Many siblings under a meta node, only the meta node is metadata of parent
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
meta = MockNode("meta", 3, parent)
siblings = [MockNode(f"sibling{i}", 3, parent) for i in range(500)]
# Only meta is metadata of parent
codeflash_output = _is_metadata_of(meta, parent) # 982ns -> 1.30μs (24.5% slower)
for sib in siblings:
codeflash_output = _is_metadata_of(sib, parent) # 232μs -> 128μs (80.6% faster)
def test_large_wide_tree_with_meta_branch():
# root -> parent -> meta -> child1 ... child500
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
meta = MockNode("meta", 3, parent)
children = [MockNode(f"child{i}", 4, meta) for i in range(500)]
# All children should be metadata of parent
for child in children:
codeflash_output = _is_metadata_of(child, parent) # 218μs -> 129μs (69.3% faster)
def test_large_wide_tree_without_meta_branch():
# root -> parent -> notmeta -> child1 ... child500
root = MockNode("root", 1)
parent = MockNode("parent", 2, root)
notmeta = MockNode("notmeta", 3, parent)
children = [MockNode(f"child{i}", 4, notmeta) for i in range(500)]
# None of the children should be metadata of parent
for child in children:
codeflash_output = _is_metadata_of(child, parent) # 283μs -> 130μs (118% faster)
def test_large_multiple_meta_nodes_different_parents():
# root -> parent1 -> meta1; root -> parent2 -> meta2
root = MockNode("root", 1)
parent1 = MockNode("parent1", 2, root)
parent2 = MockNode("parent2", 2, root)
meta1 = MockNode("meta", 3, parent1)
meta2 = MockNode("meta", 3, parent2)
# meta1 is metadata of parent1, not parent2, and vice versa
codeflash_output = _is_metadata_of(meta1, parent1) # 1.01μs -> 1.20μs (16.1% slower)
codeflash_output = _is_metadata_of(meta2, parent2) # 445ns -> 474ns (6.12% slower)
codeflash_output = _is_metadata_of(meta1, parent2) # 673ns -> 649ns (3.70% faster)
codeflash_output = _is_metadata_of(meta2, parent1) # 474ns -> 444ns (6.76% faster)
codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
To edit these changes
git checkout codeflash/optimize-_is_metadata_of-mhvwzv34and push.