Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions bin/benchmark_uninstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#!/bin/bash
# Fub Uninstaller Performance Benchmark
# Tests scanning performance with various optimizations

set -euo pipefail

# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CACHE_FILE="$HOME/.cache/fub/apps_list.txt"

# Colors
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

echo "╔════════════════════════════════════════════════════════════╗"
echo "║ Fub Uninstaller Performance Benchmark (Ubuntu 24.04) ║"
echo "╚════════════════════════════════════════════════════════════╝"
echo ""

# System information
echo -e "${CYAN}System Information:${NC}"
echo " OS: $(lsb_release -ds 2>/dev/null || echo "Unknown")"
echo " Kernel: $(uname -r)"
echo " CPU: $(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)"
echo " CPU Cores: $(nproc)"
echo " RAM: $(free -h | awk '/^Mem:/ {print $2}')"
echo ""

# Package counts
echo -e "${CYAN}Installed Packages:${NC}"
APT_COUNT=$(dpkg -l | grep "^ii" | wc -l)
SNAP_COUNT=$(snap list 2>/dev/null | tail -n +2 | wc -l || echo 0)
FLATPAK_COUNT=$(flatpak list --app 2>/dev/null | wc -l || echo 0)
APPIMAGE_COUNT=$(find "$HOME" -name "*.AppImage" -type f 2>/dev/null | wc -l || echo 0)

echo " APT packages: $APT_COUNT"
echo " Snap packages: $SNAP_COUNT"
echo " Flatpak apps: $FLATPAK_COUNT"
echo " AppImage files: $APPIMAGE_COUNT"
echo ""

# Benchmark function
benchmark_scan() {
local label="$1"
local clear_cache="${2:-false}"

# Clear cache if requested
if [[ "$clear_cache" == "true" ]]; then
rm -f "$CACHE_FILE"
fi

echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}Test: $label${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"

# Get start time
local start_time=$(date +%s.%N)

# Source the script functions
source "$SCRIPT_DIR/../lib/common.sh"
source "$SCRIPT_DIR/../lib/package_managers.sh"

# Run the scan
mkdir -p "$(dirname "$CACHE_FILE")"
source "$SCRIPT_DIR/uninstall.sh" >/dev/null 2>&1 <<EOF || true
EOF

# Use scan function directly
if [[ "$clear_cache" == "true" ]]; then
scan_all_applications "$CACHE_FILE" "true" 2>&1 | grep -v "^$" || true
else
scan_all_applications "$CACHE_FILE" "false" 2>&1 | grep -v "^$" || true
fi

# Get end time
local end_time=$(date +%s.%N)

# Calculate duration
local duration=$(echo "$end_time - $start_time" | bc)

# Get result count
local pkg_count=$(wc -l < "$CACHE_FILE" 2>/dev/null || echo 0)

echo ""
echo -e " ${GREEN}✓${NC} Duration: ${GREEN}${duration}s${NC}"
echo -e " ${GREEN}✓${NC} Packages found: ${GREEN}${pkg_count}${NC}"

# Check cache status
if [[ -f "$CACHE_FILE" ]]; then
local cache_size=$(du -h "$CACHE_FILE" | awk '{print $1}')
echo -e " ${GREEN}✓${NC} Cache size: ${GREEN}${cache_size}${NC}"
fi

echo ""
}

# Run benchmarks
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo -e "${CYAN}Starting Performance Tests...${NC}"
echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}"
echo ""

# Test 1: First scan (no cache)
benchmark_scan "First scan (cold cache, parallel mode)" "true"

sleep 1

# Test 2: Cached scan
benchmark_scan "Second scan (warm cache)" "false"

sleep 1

# Test 3: Force refresh
benchmark_scan "Force refresh (cache bypass)" "true"

sleep 1

# Test 4: Cached again
benchmark_scan "Fourth scan (warm cache again)" "false"

echo ""
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Benchmark Complete! ║${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""

# Summary
echo -e "${CYAN}Summary:${NC}"
echo " Cache location: $CACHE_FILE"
if [[ -f "$CACHE_FILE" ]]; then
echo " Cache age: $(stat -c "%y" "$CACHE_FILE" | awk '{print $1, $2}')"
echo " Cache size: $(du -h "$CACHE_FILE" | awk '{print $1}')"
fi
echo ""

echo -e "${YELLOW}Performance Tips:${NC}"
echo " • First scan builds cache (2-10 seconds)"
echo " • Cached scans are instant (<1 second)"
echo " • Cache expires after 5 minutes"
echo " • Use --refresh to force cache rebuild"
echo ""

echo -e "${GREEN}Run the uninstaller with:${NC}"
echo " ./bin/uninstall.sh"
echo ""
137 changes: 128 additions & 9 deletions bin/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,110 @@ ensure_fzf() {
return 0
}

# Scan all installed applications
# Check if cache is valid (less than 5 minutes old)
is_cache_valid() {
local cache_file="$1"
local max_age_seconds=300 # 5 minutes

if [[ ! -f "$cache_file" ]]; then
return 1
fi

local cache_age=$(($(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0)))
[[ $cache_age -lt $max_age_seconds ]]
}

# Scan all installed applications (OPTIMIZED with parallel processing and caching)
scan_all_applications() {
local output_file="$1"
local force_refresh="${2:-false}"

# Check cache validity
if [[ "$force_refresh" != "true" ]] && is_cache_valid "$output_file"; then
local cache_age=$(($(date +%s) - $(stat -c %Y "$output_file")))
echo -e "${GREEN}✓${NC} Using cached package list (${cache_age}s old)"
return 0
fi

> "$output_file"
echo -e "${BLUE}Scanning installed applications (optimized parallel mode)...${NC}"

# Temporary files for parallel processing
local tmp_dir="$HOME/.cache/fub/tmp"
mkdir -p "$tmp_dir"
local apt_tmp="$tmp_dir/apt_packages.$$"
local snap_tmp="$tmp_dir/snap_packages.$$"
local flatpak_tmp="$tmp_dir/flatpak_packages.$$"
local appimage_tmp="$tmp_dir/appimage_files.$$"

# Launch all scans in parallel for maximum performance
{
echo -e " ${BLUE}→${NC} APT packages..." >&2
scan_apt_packages_with_sizes | while IFS=$'\t' read -r pkg_name version pkg_type size_kb; do
if is_system_critical "$pkg_name"; then
continue
fi
local size_human=$(numfmt --to=iec --suffix=B --padding=7 $((size_kb * 1024)) 2>/dev/null || echo "Unknown")
echo "apt|$pkg_name|$version|$size_kb|$size_human"
done > "$apt_tmp"
} &
local apt_pid=$!

{
if command -v snap &>/dev/null; then
echo -e " ${BLUE}→${NC} Snap packages..." >&2
scan_snap_packages_with_sizes | while IFS=$'\t' read -r pkg_name version pkg_type size_kb; do
local size_human=$(numfmt --to=iec --suffix=B --padding=7 $((size_kb * 1024)) 2>/dev/null || echo "Unknown")
echo "snap|$pkg_name|$version|$size_kb|$size_human"
done > "$snap_tmp"
else
touch "$snap_tmp"
fi
} &
local snap_pid=$!

{
if command -v flatpak &>/dev/null; then
echo -e " ${BLUE}→${NC} Flatpak packages..." >&2
scan_flatpak_packages_with_sizes | while IFS=$'\t' read -r pkg_id pkg_name version pkg_type size_str; do
# Parse flatpak size
local size_kb=$(parse_flatpak_size "$size_str")
local size_human=$(numfmt --to=iec --suffix=B --padding=7 $((size_kb * 1024)) 2>/dev/null || echo "Unknown")
echo "flatpak|$pkg_id|$version|$size_kb|$size_human|$pkg_name"
done > "$flatpak_tmp"
else
touch "$flatpak_tmp"
fi
} &
local flatpak_pid=$!

{
echo -e " ${BLUE}→${NC} AppImage files..." >&2
scan_appimage_files | while IFS=$'\t' read -r appimage_path pkg_name pkg_type size_kb; do
local size_human=$(numfmt --to=iec --suffix=B --padding=7 $((size_kb * 1024)) 2>/dev/null || echo "Unknown")
echo "appimage|$pkg_name|unknown|$size_kb|$size_human|$appimage_path"
done > "$appimage_tmp"
} &
local appimage_pid=$!

# Wait for all parallel scans to complete
wait $apt_pid 2>/dev/null
wait $snap_pid 2>/dev/null
wait $flatpak_pid 2>/dev/null
wait $appimage_pid 2>/dev/null

# Merge results
cat "$apt_tmp" "$snap_tmp" "$flatpak_tmp" "$appimage_tmp" > "$output_file" 2>/dev/null

# Cleanup temp files
rm -f "$apt_tmp" "$snap_tmp" "$flatpak_tmp" "$appimage_tmp"

local total_count=$(wc -l < "$output_file")
echo -e "${GREEN}✓${NC} Found $total_count applications (parallel scan complete)"
}

# Legacy scan function (kept for compatibility)
scan_all_applications_sequential() {
local output_file="$1"
> "$output_file"

Expand Down Expand Up @@ -340,9 +442,13 @@ Found $total packages | ↑↓: Navigate | Tab: Toggle | Enter: Confirm | Esc: C

# Main function
main() {
# Check for help
if [[ "${1:-}" == "--help" ]] || [[ "${1:-}" == "-h" ]]; then
cat <<EOF
local force_refresh="false"

# Parse arguments
for arg in "$@"; do
case "$arg" in
--help|-h)
cat <<EOF
Fub Uninstaller - Ubuntu Edition (with fzf)

Interactive application uninstaller with multi-package-manager support.
Expand All @@ -360,6 +466,12 @@ Features:
- Live preview of package details
- Color-coded by package manager
- Auto-installs fzf if missing
- OPTIMIZED: Parallel scanning & 5-minute cache

Performance:
- Package managers scanned in parallel
- Batch queries for sizes (much faster)
- Smart caching (5-minute cache, auto-refresh)

Keyboard shortcuts:
↑↓ / jk Navigate list
Expand All @@ -375,26 +487,33 @@ Protected packages:
- Data-protected packages require confirmation

Usage:
uninstall.sh Launch interactive uninstaller
uninstall.sh --help Show this help
uninstall.sh Launch interactive uninstaller
uninstall.sh --refresh Force refresh package cache
uninstall.sh --help Show this help

EOF
exit 0
fi
exit 0
;;
--refresh|-r)
force_refresh="true"
;;
esac
done

# Banner
clear_screen
echo -e "${GREEN}╔═══════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Fub Uninstaller (fzf) ║${NC}"
echo -e "${GREEN}║ Complete Application Removal ║${NC}"
echo -e "${GREEN}║ ${CYAN}⚡ OPTIMIZED PARALLEL MODE ⚡${GREEN} ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════╝${NC}"
echo ""

# Scan applications
local apps_cache="$HOME/.cache/fub/apps_list.txt"
mkdir -p "$(dirname "$apps_cache")"

scan_all_applications "$apps_cache"
scan_all_applications "$apps_cache" "$force_refresh"

if [[ ! -s "$apps_cache" ]]; then
echo -e "${YELLOW}No applications found to uninstall.${NC}"
Expand Down
Loading
Loading