coreinsight-cli 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/PKG-INFO +3 -3
  2. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/main.py +5 -3
  3. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/profiler.py +186 -8
  4. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight_cli.egg-info/PKG-INFO +3 -3
  5. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight_cli.egg-info/SOURCES.txt +1 -3
  6. coreinsight_cli-0.2.1/coreinsight_cli.egg-info/top_level.txt +1 -0
  7. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/pyproject.toml +3 -3
  8. coreinsight_cli-0.2.0/coreinsight_cli.egg-info/top_level.txt +0 -2
  9. coreinsight_cli-0.2.0/coreinsight_demo/bad_loop.py +0 -38
  10. coreinsight_cli-0.2.0/coreinsight_demo/data_processor.py +0 -23
  11. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/LICENSE +0 -0
  12. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/README.md +0 -0
  13. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/Dockerfile.cpp-sandbox +0 -0
  14. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/Dockerfile.python-sandbox +0 -0
  15. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/__init__.py +0 -0
  16. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/analyzer.py +0 -0
  17. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/config.py +0 -0
  18. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/demo/__init__.py +0 -0
  19. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/demo/bad_loop.py +0 -0
  20. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/demo/data_processor.py +0 -0
  21. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/demo/slow.cpp +0 -0
  22. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/hardware.py +0 -0
  23. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/indexer.py +0 -0
  24. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/memory.py +0 -0
  25. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/parser.py +0 -0
  26. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/prompts.py +0 -0
  27. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/sandbox.py +0 -0
  28. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight/scanner.py +0 -0
  29. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight_cli.egg-info/dependency_links.txt +0 -0
  30. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight_cli.egg-info/entry_points.txt +0 -0
  31. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/coreinsight_cli.egg-info/requires.txt +0 -0
  32. {coreinsight_cli-0.2.0 → coreinsight_cli-0.2.1}/setup.cfg +0 -0
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreinsight-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Local-first AI performance profiler that mathematically verifies optimizations for Python, C++, and CUDA
5
5
  Author: Varun Jani
6
- License: MIT
6
+ License: GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/Prais3/coreinsight_cli
8
8
  Project-URL: Bug Tracker, https://github.com/Prais3/coreinsight_cli/issues
9
9
  Keywords: performance,profiling,optimization,llm,cuda,cpp,python,hpc,benchmarking
10
10
  Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
@@ -599,9 +599,11 @@ def run_analysis(file_path: str):
599
599
  "[bold]Enjoyed CoreInsight?[/bold] Pro unlocks:\n"
600
600
  " • [cyan]Cloud models[/cyan] (GPT-4o, Claude, Gemini)\n"
601
601
  " • [cyan]Unlimited functions[/cyan] per file\n"
602
- " • [cyan]5 retry attempts[/cyan] + deeper test coverage\n\n"
603
- f"[yellow]Join the waitlist:[/yellow] [cyan underline]{PRO_WAITLIST_URL}[/cyan underline]",
604
- title="⚡ Upgrade to Pro",
602
+ " • [cyan]5 retry attempts[/cyan] + deeper test coverage\n"
603
+ "[cyan]AI-free hardware profiling[/cyan] cProfile + perf stat evidence in every report\n\n"
604
+ f"[bold yellow]Pro is free during beta.[/bold yellow] Request a key:\n"
605
+ f"[cyan underline]{PRO_WAITLIST_URL}[/cyan underline]",
606
+ title="⚡ Upgrade to Pro — free during beta",
605
607
  border_style="yellow",
606
608
  ))
607
609
 
@@ -135,6 +135,10 @@ for _ in range({n_iter}):
135
135
  '''.strip()
136
136
 
137
137
 
138
+ # Minimal C++ wrapper — compiles the full source file and runs it under perf stat.
139
+ # The source file must be compilable as a standalone program (has a main or we inject one).
140
+ _CPP_PERF_COMPILE_CMD = "g++ -O3 -std=c++17 {src} -o {out}"
141
+
138
142
  # ---------------------------------------------------------------------------
139
143
  # Internal helpers
140
144
  # ---------------------------------------------------------------------------
@@ -271,7 +275,14 @@ class HardwareProfiler:
271
275
  source_dir=source_dir,
272
276
  )
273
277
  if language in ("cpp", "c++"):
274
- return self._profile_cpp(detected)
278
+ return self._profile_cpp(
279
+ detected,
280
+ original_code=original_code,
281
+ optimized_code=optimized_code,
282
+ func_name=func_name,
283
+ original_file_content=original_file_content,
284
+ source_dir=source_dir,
285
+ )
275
286
  if language in ("cuda", "cu", "cuh"):
276
287
  return self._profile_cuda(detected)
277
288
  except Exception as exc:
@@ -520,16 +531,183 @@ class HardwareProfiler:
520
531
  return metrics or None
521
532
 
522
533
  # ------------------------------------------------------------------ #
523
- # C++ path (v0.2.0: valgrind callgrind)
534
+ # C++ path — perf stat on host (v0.2.1)
524
535
  # ------------------------------------------------------------------ #
525
536
 
526
- def _profile_cpp(self, detected: Dict[str, bool]) -> ProfilerResult:
527
- found = [k for k, v in detected.items() if v]
528
- note = f"Detected: {', '.join(found)}." if found else "No C++ profiling tools found."
529
- return ProfilerResult(
530
- available=False, tool="none", language="cpp",
531
- error=f"{note} Full C++ profiling (perf stat + valgrind) coming in v0.2.0.",
537
+ def _profile_cpp(
538
+ self,
539
+ detected: Dict[str, bool],
540
+ original_code: str = "",
541
+ optimized_code: str = "",
542
+ func_name: str = "",
543
+ original_file_content: str = "",
544
+ source_dir: str = "",
545
+ n_iter: int = 1,
546
+ ) -> ProfilerResult:
547
+ result = ProfilerResult(available=False, tool="perf stat", language="cpp")
548
+
549
+ has_perf = detected.get("perf", False)
550
+ has_gpp = shutil.which("g++") is not None
551
+
552
+ if not has_perf:
553
+ result.error = (
554
+ "perf not found on host (Linux only). "
555
+ "Install via: sudo apt install linux-perf"
556
+ )
557
+ return result
558
+ if not has_gpp:
559
+ result.error = "g++ not found on host — required to compile for perf stat."
560
+ return result
561
+ if not original_file_content:
562
+ result.error = "No source file content available for C++ profiling."
563
+ return result
564
+
565
+ host_metrics = self._run_perf_stat_cpp(
566
+ original_file_content, optimized_code, func_name, n_iter
567
+ )
568
+ if host_metrics is None:
569
+ result.error = (
570
+ "perf stat produced no counters — check kernel.perf_event_paranoid "
571
+ "(sudo sysctl kernel.perf_event_paranoid=1) or compilation errors."
572
+ )
573
+ return result
574
+
575
+ result.available = True
576
+ result.host_tool_name = "perf stat"
577
+ result.host_tool_metrics = host_metrics
578
+ return result
579
+
580
+ def _substitute_cpp_function(
581
+ self, source: str, func_name: str, new_body: str
582
+ ) -> Optional[str]:
583
+ """
584
+ Best-effort substitution of a C++ function by name.
585
+ Finds the first occurrence of `func_name` followed by a parameter list
586
+ and replaces the entire brace-delimited body. Returns None on failure.
587
+ """
588
+ # Find the function signature line
589
+ sig_pattern = re.compile(
590
+ r"[^\n]*\b" + re.escape(func_name) + r"\s*\([^)]*\)[^\{]*\{",
591
+ re.DOTALL,
532
592
  )
593
+ m = sig_pattern.search(source)
594
+ if not m:
595
+ return None
596
+
597
+ # Walk forward to find the matching closing brace
598
+ start = m.start()
599
+ brace_start = source.index("{", m.start())
600
+ depth, i = 0, brace_start
601
+ while i < len(source):
602
+ if source[i] == "{":
603
+ depth += 1
604
+ elif source[i] == "}":
605
+ depth -= 1
606
+ if depth == 0:
607
+ break
608
+ i += 1
609
+ if depth != 0:
610
+ return None
611
+
612
+ # Replace everything from the signature start to the closing brace
613
+ replacement = source[start : brace_start + 1] + "\n" + new_body.strip() + "\n}"
614
+ return source[:start] + replacement + source[i + 1:]
615
+
616
+ def _run_perf_stat_cpp(
617
+ self,
618
+ original_file_content: str,
619
+ optimized_code: str,
620
+ func_name: str,
621
+ n_iter: int,
622
+ ) -> Optional[List[ProfilerMetric]]:
623
+ events = "cache-misses,cache-references,instructions,cycles,branch-misses"
624
+ counters_per_label: Dict[str, Dict[str, float]] = {}
625
+
626
+ # Build optimized source — substitute function if we can, else skip opt run
627
+ opt_source = None
628
+ if optimized_code and func_name:
629
+ opt_source = self._substitute_cpp_function(
630
+ original_file_content, func_name, optimized_code
631
+ )
632
+
633
+ sources = [("original", original_file_content)]
634
+ if opt_source:
635
+ sources.append(("optimized", opt_source))
636
+
637
+ tmp = tempfile.mkdtemp()
638
+ try:
639
+ for label, src in sources:
640
+ src_path = os.path.join(tmp, f"{label}.cpp")
641
+ bin_path = os.path.join(tmp, label)
642
+ with open(src_path, "w") as fh:
643
+ fh.write(src)
644
+
645
+ # Compile
646
+ compile_proc = subprocess.run(
647
+ ["g++", "-O3", "-std=c++17", src_path, "-o", bin_path],
648
+ capture_output=True, text=True, timeout=60,
649
+ )
650
+ if compile_proc.returncode != 0:
651
+ logger.debug(
652
+ f"C++ perf: compile failed for {label}:\n"
653
+ f"{compile_proc.stderr[:400]}"
654
+ )
655
+ return None
656
+
657
+ # Run under perf stat
658
+ try:
659
+ perf_proc = subprocess.run(
660
+ ["perf", "stat", "-e", events, bin_path],
661
+ capture_output=True, text=True, timeout=120,
662
+ )
663
+ parsed = _parse_perf_stat(perf_proc.stderr)
664
+ if not parsed:
665
+ logger.debug(
666
+ f"C++ perf: no counters for {label}. "
667
+ f"stderr: {perf_proc.stderr[:200]}"
668
+ )
669
+ return None
670
+ counters_per_label[label] = parsed
671
+ except subprocess.TimeoutExpired:
672
+ logger.debug("C++ perf stat timed out.")
673
+ return None
674
+ except PermissionError:
675
+ logger.debug("C++ perf stat: permission denied.")
676
+ return None
677
+
678
+ except Exception as exc:
679
+ logger.debug(f"C++ perf stat error: {exc}")
680
+ return None
681
+ finally:
682
+ shutil.rmtree(tmp, ignore_errors=True)
683
+
684
+ if "original" not in counters_per_label:
685
+ return None
686
+
687
+ orig_c = counters_per_label["original"]
688
+ # If substitution failed we only have original — report as baseline only
689
+ opt_c = counters_per_label.get("optimized", orig_c)
690
+
691
+ display_map = [
692
+ ("cache_misses", "Cache misses", "lower is better"),
693
+ ("cache_refs", "Cache refs", ""),
694
+ ("instructions", "Instructions", "lower is better"),
695
+ ("cycles", "CPU cycles", "lower is better"),
696
+ ("branch_misses", "Branch misses", "lower is better"),
697
+ ]
698
+ metrics: List[ProfilerMetric] = []
699
+ for key, display, note in display_map:
700
+ if key in orig_c:
701
+ ov = orig_c[key]
702
+ opv = opt_c.get(key, ov)
703
+ metrics.append(ProfilerMetric(
704
+ name=display,
705
+ original=f"{ov:,.0f}",
706
+ optimized=f"{opv:,.0f}",
707
+ delta=_pct_delta(ov, opv) if opv != ov else "—",
708
+ note=note,
709
+ ))
710
+ return metrics or None
533
711
 
534
712
  # ------------------------------------------------------------------ #
535
713
  # CUDA path (v0.2.0: nsys / nvprof)
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreinsight-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Local-first AI performance profiler that mathematically verifies optimizations for Python, C++, and CUDA
5
5
  Author: Varun Jani
6
- License: MIT
6
+ License: GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/Prais3/coreinsight_cli
8
8
  Project-URL: Bug Tracker, https://github.com/Prais3/coreinsight_cli/issues
9
9
  Keywords: performance,profiling,optimization,llm,cuda,cpp,python,hpc,benchmarking
10
10
  Classifier: Development Status :: 3 - Alpha
11
11
  Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
@@ -24,6 +24,4 @@ coreinsight_cli.egg-info/SOURCES.txt
24
24
  coreinsight_cli.egg-info/dependency_links.txt
25
25
  coreinsight_cli.egg-info/entry_points.txt
26
26
  coreinsight_cli.egg-info/requires.txt
27
- coreinsight_cli.egg-info/top_level.txt
28
- coreinsight_demo/bad_loop.py
29
- coreinsight_demo/data_processor.py
27
+ coreinsight_cli.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ coreinsight
@@ -4,9 +4,9 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "coreinsight-cli"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Local-first AI performance profiler that mathematically verifies optimizations for Python, C++, and CUDA"
9
- license = {text = "MIT"}
9
+ license = {text = "GPL-3.0-or-later"}
10
10
  authors = [
11
11
  {name = "Varun Jani"}
12
12
  ]
@@ -14,7 +14,7 @@ keywords = ["performance", "profiling", "optimization", "llm", "cuda", "cpp", "p
14
14
  classifiers = [
15
15
  "Development Status :: 3 - Alpha",
16
16
  "Intended Audience :: Developers",
17
- "License :: OSI Approved :: MIT License",
17
+ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
18
18
  "Programming Language :: Python :: 3",
19
19
  "Programming Language :: Python :: 3.9",
20
20
  "Programming Language :: Python :: 3.10",
@@ -1,2 +0,0 @@
1
- coreinsight
2
- coreinsight_demo
@@ -1,38 +0,0 @@
1
- def count_unique_numbers(numbers):
2
- """
3
- Inefficiently counts unique numbers in a list.
4
- This implementation is intentionally unoptimized.
5
- """
6
- unique = []
7
-
8
- for i in range(len(numbers)):
9
- already_exists = False
10
-
11
- for j in range(len(unique)):
12
- if numbers[i] == unique[j]:
13
- already_exists = True
14
- break
15
-
16
- if not already_exists:
17
- unique.append(numbers[i])
18
-
19
- return len(unique)
20
-
21
-
22
- def slow_sum_of_squares(n):
23
- """
24
- Intentionally slow calculation of sum of squares up to n.
25
- """
26
- total = 0
27
- for i in range(n):
28
- square = 0
29
- for _ in range(i):
30
- square += i # Repeated addition instead of i * i
31
- total += square
32
- return total
33
-
34
-
35
- if __name__ == "__main__":
36
- nums = [1, 2, 2, 3, 4, 4, 5, 1, 6, 7, 7, 8, 9, 9]
37
- print("Unique count:", count_unique_numbers(nums))
38
- print("Slow sum of squares:", slow_sum_of_squares(1000))
@@ -1,23 +0,0 @@
1
- from bad_loop import count_unique_numbers
2
-
3
- def process_large_dataset(dataset):
4
- """
5
- Simulates processing a large dataset.
6
- This function has a hidden O(N^2) list insertion bottleneck,
7
- and it relies on an external unoptimized function.
8
- """
9
- unique_count = count_unique_numbers(dataset)
10
-
11
- result_buffer = []
12
-
13
- # BOTTLENECK: Inserting at index 0 in a Python list forces
14
- # the interpreter to shift all existing elements in memory O(N^2)
15
- for i in range(unique_count * 10):
16
- result_buffer.insert(0, i)
17
-
18
- return sum(result_buffer)
19
-
20
-
21
- if __name__ == "__main__":
22
- sample_data = [1, 2, 2, 3, 4, 5, 5, 6] * 10
23
- print("Processed sum:", process_large_dataset(sample_data))
File without changes