Performance Optimization Guide
Source:vignettes/performance-optimization.Rmd
performance-optimization.Rmdpladdrr Performance Optimization Guide
This guide explains the 3-tier performance API in pladdrr and how to choose the right level for your needs.
The Three Performance Tiers
pladdrr provides three API tiers, each trading interface simplicity for lower per-call overhead:
┌─────────────────────────────────────────────────────────┐
│ TIER 1: High-Level API (Sound, Pitch, Formant, etc.) │
│ - Full object-oriented interface │
│ - Method chaining: sound$to_pitch()$get_mean() │
│ - Best for: Interactive analysis, small datasets │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ TIER 2: Direct API (*_direct functions) │
│ - Accept XPtr directly, skip object dispatch │
│ - Best for: Tight loops, repeated queries │
│ - Reduces per-call R dispatch overhead │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ TIER 3: Batch/Parallel API (*_batch, *_parallel) │
│ - Designed for bulk operations │
│ - Best for: Large datasets, production workflows │
│ - Replaces many R->C crossings with one per call │
└─────────────────────────────────────────────────────────┘
When do the tiers actually matter?
It depends entirely on how much work each call does relative to the R-to-C++ boundary it crosses.
- Querying in a tight loop — thousands of cheap point queries — is where the batch and direct APIs are worth reaching for, because the per-call dispatch overhead is a larger share of the total cost. Going from one crossing per query to one crossing per vector changes the shape of that cost.
-
A single analysis call —
to_pitch(),to_formant_burg(),calculate_cpps_fast()— is dominated by the underlying DSP work, and the tier you call it through makes little practical difference. The different CPPS entry points (sound$to_powercepstrogram()$get_cpps(),calculate_cpps_fast(),calculate_cpps_ultra()) all compute the same value using the same core algorithm.
Rule of thumb: reach for a batch or direct function when you are
about to write a loop over query points or files. Switching analysis
entry points for a single call is not expected to help — measure your
own workload with system.time() if you’re unsure.
Tier 1: High-Level API (Best for Most Users)
When to use: Interactive analysis, exploration, small datasets (<100 files)
Characteristics: - Intuitive object-oriented interface - Method chaining - Full error checking and validation - Easy to read and maintain
library(pladdrr)
#> pladdrr: direct access to Praat's core algorithms from R.
#> See ?pladdrr for an overview, or citation("pladdrr") for citation details.
wav_file <- system.file("extdata", "test.wav", package = "pladdrr")
# Load sound
sound <- Sound(wav_file)
# Extract pitch (returns Pitch object)
pitch <- sound$to_pitch(pitch_floor = 75, pitch_ceiling = 300)
# Query statistics
mean_f0 <- pitch$get_mean(0, 0, "hertz")
sd_f0 <- pitch$get_standard_deviation(0, 0, "hertz")
# Method chaining
mean_f1 <- sound$to_formant()$get_mean(1, 0, 0, "hertz")Adequate for most use cases. Only reach for Tier 2/3 if profiling your own code shows a bottleneck.
Tier 2: Direct API
When to use: Processing >100 files, tight loops, production code
Characteristics: - Functions accept external pointers (XPtr) directly - Skip R6 object dispatch overhead - Return raw values or pointers - Require manual pointer extraction
# Extract pointer once
sound_ptr <- sound$.xptr
# Direct conversion (returns XPtr, not Pitch object)
pitch_ptr <- to_pitch_direct(sound_ptr,
time_step = 0,
pitch_floor = 75,
pitch_ceiling = 300)
# Get statistics in one call
stats <- get_pitch_stats_direct(pitch_ptr)
# Returns: list(min, max, mean, stdev, median, q25, q75, count_voiced)
# Get F1-F4 in one call
formant_ptr <- to_formant_direct(sound_ptr)
formants <- get_formants_direct(formant_ptr, time = 0.5)
# Returns: c(F1 = 500, F2 = 1500, F3 = 2500, F4 = 3500)Fewer R→C boundary crossings
# Tier 1 approach: one boundary crossing per query
min_f0 <- pitch$get_minimum(0, 0, "hertz")
max_f0 <- pitch$get_maximum(0, 0, "hertz")
mean_f0 <- pitch$get_mean(0, 0, "hertz")
# ... 5 more calls
# Tier 2 approach: one boundary crossing for all statistics
stats <- get_pitch_stats_direct(pitch)Available Direct Functions
Conversion: - to_pitch_direct() -
Create Pitch - to_formant_direct() - Create Formant -
to_intensity_direct() - Create Intensity -
to_harmonicity_direct() - Create Harmonicity -
to_spectrum_direct() - Create Spectrum -
to_spectrogram_direct() - Create Spectrogram -
to_ltas_direct() - Create LTAS -
to_point_process_direct() - Create PointProcess
Queries: - get_pitch_stats_direct() -
All pitch statistics at once - get_formants_direct() -
F1-F4 at time point - get_pitch_value_direct() - Single
pitch value - get_intensity_value_direct() - Single
intensity value - get_formant_value_direct() - Single
formant value
Tier 3: Batch & Parallel API
When to use: Large datasets (>100 files), production pipelines
Batch Operations
Process multiple sounds in a single C++ call:
# In practice `files` would be a vector of paths to distinct recordings, e.g.
# list.files("audio/", pattern = "\\.wav$", full.names = TRUE). This example
# repeats the bundled test file to keep the vignette self-contained.
files <- rep(wav_file, 3)
# Load multiple sounds
sounds <- lapply(files, Sound)
# Tier 1 approach: one R->C crossing per sound
pitches <- lapply(sounds, function(s) s$to_pitch())
# Tier 3 approach: one R->C crossing for the whole list
pitches <- sound_to_pitch_batch(sounds)Available batch functions: -
sound_to_pitch_batch() - Batch pitch extraction -
sound_to_pitch_ac_batch() - Batch autocorrelation pitch -
sound_to_pitch_cc_batch() - Batch cross-correlation pitch -
sound_to_formant_batch() - Batch formant extraction -
sound_to_intensity_batch() - Batch intensity extraction
Vectorized Queries
Extract values at multiple time points in one call:
# Tier 1 approach: one R->C crossing per time point
times <- seq(0.1, 1.0, by = 0.01)
f0_values <- vapply(times, function(t) {
pitch$get_value_at_time(t, "hertz")
}, numeric(1))
# Tier 3 approach: one R->C crossing for the whole vector
f0_values <- get_pitch_at_times(pitch, times)Vectorized query functions: -
get_pitch_at_times() - Batch pitch queries -
get_formants_at_times() - Batch F1-F4 queries -
get_intensity_at_times() - Batch intensity queries
Parallel Processing
Distribute file-level work across multiple CPU cores:
# Analyze files in parallel. In practice `files` is a vector of paths to
# distinct recordings, e.g. list.files("audio/", pattern = "\\.wav$",
# full.names = TRUE); this example repeats the bundled test file.
files <- rep(wav_file, 4)
# Simple parallel analysis
results <- analyze_files_parallel(files, function(sound) {
pitch <- sound$to_pitch()
list(
mean_f0 = pitch$get_mean(0, 0, "hertz"),
sd_f0 = pitch$get_standard_deviation(0, 0, "hertz")
)
}, n_cores = 2)
#> Processing 4 files using 2 cores (2 thread(s)/worker)
# Convenience functions
pitches <- extract_pitch_parallel(files, n_cores = 2)
#> Processing 4 files using 2 cores (2 thread(s)/worker)
formants <- extract_formant_parallel(files, n_cores = 2)
#> Processing 4 files using 2 cores (2 thread(s)/worker)
intensities <- extract_intensity_parallel(files, n_cores = 2)
#> Processing 4 files using 2 cores (2 thread(s)/worker)Parallel functions: -
analyze_files_parallel() - Generic parallel file processing
- process_sounds_parallel() - Parallel processing of loaded
sounds - extract_pitch_parallel() - Parallel pitch
extraction - extract_formant_parallel() - Parallel formant
extraction - extract_intensity_parallel() - Parallel
intensity extraction - benchmark_parallel() - Measure your
own workload across core counts
Tier Comparison Examples
The examples below use system.time() so you can measure
each approach against your own files and hardware — the numbers vary too
much by machine, file size, and workload to be worth quoting here.
Example 1: Single File Analysis
sound <- Sound(wav_file)
# Tier 1: Standard API
system.time({
pitch <- sound$to_pitch()
mean_f0 <- pitch$get_mean(0, 0, "hertz")
sd_f0 <- pitch$get_standard_deviation(0, 0, "hertz")
min_f0 <- pitch$get_minimum(0, 0, "hertz")
max_f0 <- pitch$get_maximum(0, 0, "hertz")
})
#> user system elapsed
#> 0.012 0.000 0.006
# measure on your own data
# Tier 2: Direct API
system.time({
pitch_ptr <- to_pitch_direct(sound$.xptr)
stats <- get_pitch_stats_direct(pitch_ptr)
})
#> user system elapsed
#> 0.010 0.000 0.004
# fewer crossings; measure on your own dataExample 2: Batch Processing
# In practice `files` is a vector of paths to distinct recordings, e.g.
# list.files("audio/", full.names = TRUE)[1:100]; this example repeats the
# bundled test file to keep the vignette self-contained and fast to build.
files <- rep(wav_file, 20)
# Tier 1: Sequential
system.time({
sounds <- lapply(files, Sound)
pitches <- lapply(sounds, function(s) s$to_pitch())
})
#> user system elapsed
#> 0.208 0.007 0.102
# measure on your own data
# Tier 3: Batch
system.time({
sounds <- lapply(files, Sound)
pitches <- sound_to_pitch_batch(sounds)
})
#> user system elapsed
#> 0.207 0.003 0.096
# scales with cores; measure on your own data
# Tier 3: Parallel (2 cores)
system.time({
pitches <- extract_pitch_parallel(files, n_cores = 2)
})
#> Processing 20 files using 2 cores (2 thread(s)/worker)
#> user system elapsed
#> 0.105 0.081 0.379
# includes file I/O; measure on your own dataExample 3: Formant Tracking Over Time
sound <- Sound(wav_file)
formant <- sound$to_formant()
times <- seq(0.1, 0.5, by = 0.001)
# Tier 1: Loop
system.time({
f1_values <- vapply(times, function(t) {
formant$get_value_at_time(1, t, "hertz")
}, numeric(1))
})
#> user system elapsed
#> 0.013 0.000 0.012
# one R->C crossing per time point
# Tier 3: Vectorized
system.time({
f1_values <- get_formants_at_times(formant, times, formant_numbers = 1)
})
#> user system elapsed
#> 0.001 0.000 0.001
# one R->C crossing for the whole vector — this is where batching pays offDecision Tree: Which Tier Should I Use?
Are you processing < 10 files?
├─ YES → Use Tier 1 (standard API)
└─ NO ↓
Are you processing < 100 files?
├─ YES → Use Tier 2 (direct API) if profiling shows it helps
└─ NO ↓
Are you processing > 100 files?
├─ Single machine → Use Tier 3 batch + parallel
└─ Cluster → Use Tier 2 direct + your cluster manager
Best Practices
1. Start with Tier 1, Optimize Later
Don’t prematurely optimize. Use the standard API first:
# Good: Clear and maintainable
pitch <- sound$to_pitch()$smooth()
mean_f0 <- pitch$get_mean(0, 0, "hertz")Only move to Tier 2/3 when: - Profiling your own code shows a bottleneck - You’re processing >100 files - Runtime matters for your workflow
2. Batch Similar Operations
# Tier 1: Multiple individual queries
f1 <- formant$get_value_at_time(1, 0.5, "hertz")
f2 <- formant$get_value_at_time(2, 0.5, "hertz")
f3 <- formant$get_value_at_time(3, 0.5, "hertz")
f4 <- formant$get_value_at_time(4, 0.5, "hertz")
# Tier 2: Single batch query
formants <- get_formants_direct(formant, time = 0.5)3. Reuse Pointers in Loops
# Avoid: Repeated single-value queries in a loop
for (time in times) {
val <- get_pitch_value_direct(pitch, time)
}
# Prefer: One call for the whole vector of times
# (get_pitch_at_times() takes the Pitch object itself, not an XPtr — it
# extracts the pointer internally)
values <- get_pitch_at_times(pitch, times)4. Choose a Core Count That Fits Your Workload
Not all workloads scale linearly with core count, and small files can
make parallelization overhead outweigh any benefit. Use
benchmark_parallel() to test a range of core counts on a
representative subset of your own files before committing to a setting
for a full run:
# Test different core counts on a subset
benchmark_results <- benchmark_parallel(
files[1:10], # Use subset for a quick test
function(s) s$to_pitch()$get_mean(0, 0, "hertz"),
core_counts = c(1, 2) # extend to c(1, 2, 4, 8) etc. up to your machine's cores
)
#> Testing with 1 core(s)...
#> Using single core (set n_cores > 1 for parallel processing)
#> Testing with 2 core(s)...
#> Processing 10 files using 2 cores (2 thread(s)/worker)
print(benchmark_results)
#> cores time_sec speedup
#> 1 1 0.05291533 1.0000000
#> 2 2 0.06421876 0.8239856
# Inspect the returned table to see where returns diminish on your machine5. Combine Batch and Parallel Processing
# Define the per-file constants *inside* the worker function rather than
# capturing them from the enclosing environment. On Windows (and on macOS,
# where analyze_files_parallel() also uses a PSOCK cluster to avoid known
# fork/event-loop issues), each worker is a separate R process that only
# receives the function and file path — free variables referenced from the
# calling environment are not automatically exported and would error with
# "object not found" unless passed via clusterExport() or defined locally
# like this.
analyze_files_parallel(files, function(sound) {
start_times <- c(0.1, 0.3)
end_times <- c(0.2, 0.4)
analysis_times <- seq(0.01, 0.05, by = 0.01)
# Use batch operations within each worker
parts <- sound$extract_parts_batch(start_times, end_times)
formants <- sound_to_formant_batch(parts)
# Vectorized queries
lapply(formants, function(f) {
get_formants_at_times(f, analysis_times)
})
}, n_cores = 2)
#> Processing 20 files using 2 cores (2 thread(s)/worker)
#> [[1]]
#> [[1]][[1]]
#> [[1]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[1]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[1]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[1]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[1]][[2]]
#> [[1]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[1]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[1]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[1]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[2]]
#> [[2]][[1]]
#> [[2]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[2]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[2]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[2]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[2]][[2]]
#> [[2]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[2]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[2]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[2]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[3]]
#> [[3]][[1]]
#> [[3]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[3]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[3]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[3]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[3]][[2]]
#> [[3]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[3]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[3]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[3]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[4]]
#> [[4]][[1]]
#> [[4]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[4]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[4]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[4]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[4]][[2]]
#> [[4]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[4]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[4]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[4]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[5]]
#> [[5]][[1]]
#> [[5]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[5]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[5]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[5]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[5]][[2]]
#> [[5]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[5]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[5]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[5]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[6]]
#> [[6]][[1]]
#> [[6]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[6]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[6]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[6]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[6]][[2]]
#> [[6]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[6]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[6]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[6]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[7]]
#> [[7]][[1]]
#> [[7]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[7]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[7]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[7]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[7]][[2]]
#> [[7]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[7]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[7]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[7]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[8]]
#> [[8]][[1]]
#> [[8]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[8]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[8]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[8]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[8]][[2]]
#> [[8]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[8]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[8]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[8]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[9]]
#> [[9]][[1]]
#> [[9]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[9]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[9]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[9]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[9]][[2]]
#> [[9]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[9]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[9]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[9]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[10]]
#> [[10]][[1]]
#> [[10]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[10]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[10]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[10]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[10]][[2]]
#> [[10]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[10]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[10]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[10]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[11]]
#> [[11]][[1]]
#> [[11]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[11]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[11]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[11]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[11]][[2]]
#> [[11]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[11]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[11]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[11]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[12]]
#> [[12]][[1]]
#> [[12]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[12]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[12]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[12]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[12]][[2]]
#> [[12]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[12]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[12]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[12]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[13]]
#> [[13]][[1]]
#> [[13]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[13]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[13]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[13]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[13]][[2]]
#> [[13]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[13]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[13]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[13]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[14]]
#> [[14]][[1]]
#> [[14]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[14]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[14]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[14]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[14]][[2]]
#> [[14]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[14]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[14]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[14]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[15]]
#> [[15]][[1]]
#> [[15]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[15]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[15]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[15]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[15]][[2]]
#> [[15]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[15]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[15]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[15]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[16]]
#> [[16]][[1]]
#> [[16]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[16]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[16]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[16]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[16]][[2]]
#> [[16]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[16]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[16]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[16]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[17]]
#> [[17]][[1]]
#> [[17]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[17]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[17]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[17]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[17]][[2]]
#> [[17]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[17]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[17]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[17]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[18]]
#> [[18]][[1]]
#> [[18]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[18]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[18]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[18]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[18]][[2]]
#> [[18]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[18]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[18]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[18]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[19]]
#> [[19]][[1]]
#> [[19]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[19]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[19]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[19]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[19]][[2]]
#> [[19]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[19]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[19]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[19]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379
#>
#>
#>
#> [[20]]
#> [[20]][[1]]
#> [[20]][[1]]$F1
#> [1] NA NA 420.8500 420.7663 421.1240
#>
#> [[20]][[1]]$F2
#> [1] NA NA 464.7808 464.6990 465.0480
#>
#> [[20]][[1]]$F3
#> [1] NA NA 2999.369 2975.049 3042.348
#>
#> [[20]][[1]]$F4
#> [1] NA NA 4205.872 4190.150 4248.532
#>
#>
#> [[20]][[2]]
#> [[20]][[2]]$F1
#> [1] NA NA 421.0817 421.0195 420.9680
#>
#> [[20]][[2]]$F2
#> [1] NA NA 465.0067 464.9466 464.8963
#>
#> [[20]][[2]]$F3
#> [1] NA NA 3031.412 3089.364 3011.475
#>
#> [[20]][[2]]$F4
#> [1] NA NA 4239.889 4211.581 4289.379Troubleshooting
Parallel Processing Issues
macOS/Linux: - Uses mclapply()
(fork-based parallelism) - Shares memory between processes
Windows: - Uses parLapply()
(socket-based parallelism) - Creates separate R processes - May need to
export objects explicitly
Memory constraints: - Each core loads files
independently - With 4 cores and 100MB files, plan for roughly 400MB RAM
- Reduce n_cores if you hit memory limits
Runtime Not Improving?
If parallelizing or batching your own workload isn’t helping, check:
- I/O bound? - File reading may be the bottleneck, not analysis
- Small files? - Parallelization overhead can exceed any benefit
- Disk speed? - Try processing from SSD instead of HDD
-
CPU usage - Use
htopor Task Manager to see whether cores are saturated
Summary Table
| Tier | Use Case | Functions | Learning Curve |
|---|---|---|---|
| 1 | Interactive, <10 files | sound$to_pitch() |
Easy |
| 2 | Loops, 10-100 files | to_pitch_direct() |
Medium |
| 3 | Production, >100 files | sound_to_pitch_batch() |
Medium |
| 3+ | Large datasets | extract_pitch_parallel() |
Easy |
Further Reading
-
vignette("getting-started")- Introduction to pladdrr -
?analyze_files_parallel- Parallel processing documentation -
?get_pitch_stats_direct- Direct API reference