What this quiz covers
This quiz focuses on Vectorization Performance, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
Two functions transform numeric vectors. One uses x * 2 + 1; the other uses a preallocated for loop. On vectors of length two, repeated benchmarks show only a small difference, although the vectorized function is much faster on long vectors.
Which explanation best fits this pattern?
R Programming Quiz
Practice Vectorization Performance in R Programming with focused quiz questions that help you check what you know, review explanations, and build confidence with test-style prompts.
This quiz focuses on Vectorization Performance, giving you a quick way to practice the rules, question types, and explanations that matter most for R Programming.
Try each quiz question before looking at the correct answer. Use the explanations to review missed ideas, then come back to similar questions until the pattern feels familiar.
Two functions transform numeric vectors. One uses x * 2 + 1; the other uses a preallocated for loop. On vectors of length two, repeated benchmarks show only a small difference, although the vectorized function is much faster on long vectors.
Which explanation best fits this pattern?
x * 2 + 1) and the preallocated for loop are each paying nearly identical fixed costs — setting up the function environment, allocating the result vector, returning the value — while the actual computational work (two multiplications and two additions) is trivially small either way. The vectorized version's advantage lies in avoiding per-iteration R-level interpreter overhead, but when there are only two iterations, that savings is negligible compared to shared overhead. As vector length grows, the loop's per-element interpreter cost compounds while vectorization's fixed cost amortizes across thousands of elements, explaining why the gap widens dramatically on long vectors. A is correct because it precisely captures this fixed-cost-dominates-small-input dynamic.
B is wrong because R has no such deferred evaluation threshold — vectorized operations run natively via compiled C code immediately, regardless of length. C describes a fictional caching mechanism; R does not automatically memoize function outputs, and no such cache-disabling behavior exists for long vectors. D is wrong because vectorized arithmetic has roughly constant (not quadratic) setup cost — the overhead is O(1) or at most O(n) in memory allocation, never quadratic.
A useful rule of thumb: whenever a benchmark shows two approaches performing similarly at small scales but diverging at large scales, look for per-element versus fixed costs as the explanation.An explicit numeric loop is compiled with compiler::cmpfun(). After compilation it becomes noticeably faster and narrows, but does not eliminate, the gap with a vectorized base-R expression.
Which explanation best accounts for this result?
compiler::cmpfun() targets the first layer. It translates your R loop into bytecode, reducing the per-iteration cost of parsing, dispatching, and interpreting R expressions. This is a real speedup — but it only tightens the loop's overhead; it doesn't change what the loop is doing structurally. Vectorized base-R functions like sum() or cumsum(), by contrast, drop immediately into tightly optimized C code that iterates over contiguous memory with minimal overhead and often benefits from CPU-level optimizations. That's why D is correct: byte compilation closes the gap by cutting interpreter tax, but vectorized primitives maintain an edge through their own compiled, whole-vector loops.
A is wrong because byte compilation absolutely does not convert a loop into a constant-time operation — it still iterates O(n) times. The vectorized function also scans the entire vector, so this framing is backwards. B is wrong because byte compilation has nothing to do with parallelism or multi-core execution; it's purely a single-threaded optimization. C is wrong on two counts: byte compilation does not remove all allocations or bounds checks, and dismissing a consistent, reproducible performance gap as "measurement error" is not scientifically sound.
A useful study rule: whenever a question contrasts loops with vectorized code, ask yourself where each option's speedup would actually come from — interpreter level, memory level, or hardware level. That distinction almost always points to the right answer.A programmer replaces for (i in seq_along(files)) result[[i]] <- analyze(files[[i]]) with lapply(files, analyze). The two versions have nearly identical running times because analyze() is an R function that performs substantial work for each file.
Which explanation best accounts for the limited speed improvement?
lapply() first combines all files into one vector, and the cost of that combination cancels its vectorization advantage.lapply() compiles analyze() separately for every file, making its setup cost comparable to an explicit loop.lapply() uses the same source-level for statement, so there can never be any performance difference between them.lapply() manages iteration internally but still invokes the R callback per element, whose cost can dominate execution. (correct answer)lapply() to an explicit for loop in R, the key question to ask is: where does the actual time go? If the work inside each iteration is expensive, the mechanism controlling iteration matters far less than the work itself.
This is exactly what option D captures. lapply() does manage iteration in C internally, which avoids some of R's interpreter overhead — but it still must call your R function analyze() once per element. When analyze() performs substantial computation, that per-call cost completely dominates the total runtime. The small savings from C-level looping become negligible compared to the heavy lifting inside the callback. This is why you observe nearly identical running times: the bottleneck is the work, not the loop mechanism.
Option A is wrong because lapply() does not concatenate inputs into a single vector before processing. It iterates over elements individually, so no such combination cost exists. Option B introduces a fictional concept — R does not recompile functions on each lapply() call. analyze() is parsed and compiled once; repeated invocation doesn't trigger repeated compilation. Option C is technically false in the opposite direction: lapply() is not implemented as a source-level for loop. It uses a C-level loop internally, which is why it can be faster for lightweight operations — but that advantage simply doesn't show up when the callback dominates.
The study tip here: whenever a question asks why lapply() doesn't speed things up, ask yourself what fraction of time the callback function consumes versus the iteration mechanism. If the callback is slow, no amount of loop optimization will matter much.For one input size, an explicit R loop takes about 80 milliseconds and a vectorized version takes about 10 milliseconds. When the input size is doubled, the loop takes about 160 milliseconds and the vectorized version takes about 20 milliseconds.
Which conclusion is most consistent with these measurements?
for statement on each iteration.for statement is interpreted once per iteration, but that contributes a constant per-element cost — it doesn't cause quadratic behavior on its own.
Answer D is wrong because two algorithms solving the same problem can absolutely differ by a constant factor. Vectorized functions like sum() or * are implemented in compiled C code, which operates far more efficiently than R's interpreted loop overhead. The factor difference reflects implementation, not correctness.
Your study tip: always check how runtime grows, not just how much faster one approach is. Linear means doubling input doubles time — memorize that pattern and you'll cut through most complexity questions quickly.A programmer sums a large double vector with total <- total + x[i] inside a preallocated, otherwise simple R loop. Replacing the loop with sum(x) produces the same result for the tested data and is much faster.
Why can sum(x) have a large performance advantage even though it must still inspect every element?
sum() sidestep this entirely.
Here's why B is correct: sum(x) is implemented in compiled C code inside R's internals. When you call it, R hands the entire vector to a tight C loop that performs addition with no R interpreter involvement per element. Your manual loop, by contrast, executes x[i] (an R indexing call), total + x[i] (an R addition), and total <- ... (an R assignment) on every single iteration — each carrying interpreter overhead. Both approaches visit every element, but sum() does so with a fraction of the per-element cost.
A is wrong because sum() does not convert the vector to a scalar first or skip elements — it reads all of them. This describes a nonexistent shortcut.
C is wrong because there is no algorithmic shortcut here. Both the loop and sum() are O(n); the advantage is constant-factor overhead reduction, not a lower growth rate. A "stabilized running total" that stops processing simply doesn't exist for general floating-point sums.
D is wrong because base R's sum() does not automatically parallelize — it runs sequentially in C. Parallelism would require explicit packages like parallel or foreach.
Study tip: On R performance questions, always ask where the code executes — R interpreter vs. compiled C. That distinction explains most speed differences between loops and vectorized or built-in functions.A programmer compares two functions for a large numeric vector x. The first preallocates out and executes out[i] <- sqrt(x[i]) + 1 inside a for loop. The second returns sqrt(x) + 1. Both functions produce the same values.
Which explanation best accounts for the second function usually running faster?
sqrt() evaluates only selected elements when passed a vector.for loop always uses one core.for loop it compounds with every iteration.
This is exactly why B is correct. When you call sqrt(x) + 1 on a vector, R hands the entire computation off to internal C routines. Those routines iterate over the elements in compiled machine code — no repeated parsing, no per-iteration function dispatch, no interpreter overhead. The loop still happens; it just happens at the C level, which is dramatically faster than R's interpreter repeating that work thousands of times.
A is wrong because sqrt() does not skip elements when passed a vector — it computes the square root of every element, just as the loop does. There is no selective evaluation shortcut.
C is a tempting distractor because parallelism sounds like a reasonable performance explanation, but base R's vectorized functions are not automatically parallelized. They run on a single core by default, just like a for loop. Parallelism requires explicit packages like parallel or future.
D mischaracterizes the complexity. Processing n elements still scales linearly whether you use a loop or a vectorized call — the operation is O(n) either way. The speedup comes from constant factors (compiled vs. interpreted overhead), not from a change in asymptotic growth.
Study tip: Whenever a question asks why vectorization is faster in R, the answer almost always points to compiled internals and reduced interpreter overhead — not parallelism or skipped computations.A loop calculates y[i] <- 0.7 * y[i - 1] + x[i], so each result depends on the preceding result. A programmer attempts to accelerate it with one elementwise expression using shifted portions of y and x.
What is the most accurate assessment of this attempted vectorization?
y is preallocated, because preallocation causes R to perform vector assignments strictly from left to right.y[3], you need the already updated y[2], which itself required the updated y[1]. A single elementwise expression like y[-1] <- 0.7 * y[-length(y)] + x[-1] evaluates the entire right-hand side using the original values of y before any assignment occurs. This breaks the chain of dependencies, producing incorrect results for all but the first step. Answer B is correct: the attempted vectorization is generally not equivalent, and for true recurrences, you'd want a compiled routine (like those in the dsp or signal packages, or a custom Rcpp solution) that processes values sequentially while avoiding R's interpreter overhead.
Answer A is wrong because R does not interleave assignment with evaluation element by element — the entire right-hand side is fully evaluated first, then assigned. Answer C is wrong because preallocation affects memory efficiency, not evaluation order; it does not cause R to assign values left-to-right during a vectorized expression. Answer D is wrong because recurrences are not inherently quadratic — compiled sequential implementations run in linear time; the problem is correctness, not computational complexity.
Study tip: When evaluating vectorization, always check for sequential dependencies. If output i feeds into output i+1, a single vectorized expression cannot safely replace the loop.An initial loop builds a result using out <- c(out, f(x[i])) on every iteration. It is much slower than a vectorized expression. After the programmer preallocates out <- numeric(length(x)) and assigns by index, the loop becomes much faster but remains slower than the vectorized version.
Which interpretation best explains both changes in performance?
numeric() skips missing values, while vectorization avoids checking for them entirely.out <- c(out, f(x[i])) forces R to allocate a brand-new vector on every single iteration, copy all existing elements into it, then append the new one. For a vector of length n, this produces roughly O(n2) total copy operations. Preallocating with out <- numeric(length(x)) eliminates that cascading copy problem — now each iteration only writes one value into an already-existing slot, bringing memory overhead down to O(n). That explains the first speedup. The remaining gap versus a fully vectorized expression exists because the loop still visits the R interpreter on every iteration — function call dispatch, type checking, and bookkeeping overhead accumulate n times. A vectorized operation pushes all of that work into a single compiled C-level routine, bypassing per-iteration interpreter costs entirely. Answer D captures both mechanisms cleanly and correctly.
Answer A is wrong because preallocation does nothing to enable parallelism or multi-core execution — it simply fixes the memory growth problem; those are entirely separate concerns. Answer B invents a fictional explanation: numeric() initializes zeros and has no special logic for missing values, and that has nothing to do with the performance difference. Answer C is wrong in the opposite direction — it understates the impact of preallocation, which actually changes the algorithmic complexity class, not just a minor constant factor.
As a study tip, remember that R performance questions often layer two distinct costs: memory allocation strategy and interpreter overhead. Recognize that preallocation solves the first but not the second.For a very large vector, a programmer writes exp(x) / (1 + exp(x)). A carefully written preallocated loop that computes exp(x[i]) once per element unexpectedly runs faster and uses much less memory.
What is the best explanation for this result?
exp(x).exp(x) / (1 + exp(x)) actually does in R. First, it computes exp(x), allocating a full vector of length n. Then it computes exp(x) again as a separate subexpression — another full n-length vector. Finally, 1 + exp(x) creates yet another temporary. For a large vector, you've allocated roughly 3× the memory of x and called the expensive exp function twice per element. A preallocated loop that stores exp(x[i]) in a local variable computes it exactly once per element and never creates large intermediates, so C is correct: the vectorized form repeats an expensive calculation and generates temporaries that can outweigh its lower function-dispatch overhead.
A is wrong because exp() uses the same underlying math library routines whether called on a scalar or a vector — there's no alternate "scalar" algorithm that exits early. B is wrong because vectorized operations in R are linear, not quadratic; they process each element once without rescanning prior elements. D is wrong because R does not automatically parallelize for-loops — parallelism requires explicit packages like parallel or foreach. The loop's speed advantage here comes purely from reduced allocation and computation, not threading.
As a study tip: whenever you see a vectorized expression that reuses a subexpression, ask yourself how many times that computation runs and how many temporary vectors get allocated. Rewriting with a local variable or using plogis(x) (R's built-in logistic function) is often the real fix.A profiler reports that log(x) + x^2 uses only one processor core, yet it is substantially faster than an explicit R loop computing the same values.
Which statement best reconciles these observations?
log() and arithmetic operators are implemented in pre-compiled C or Fortran code. Even running on a single core, this compiled code executes a tight, cache-friendly loop over contiguous memory — avoiding the per-iteration overhead that R's interpreter incurs for every step of an explicit for loop. That overhead includes parsing, type-checking, and dispatching operations repeatedly. Vectorization eliminates that per-element tax, which is why C answers this correctly: vectorization achieves its speed through compiled sequential execution and efficient memory access, not parallel processing.
A is wrong because it assumes vectorization's speed requires multiple cores — this is a common misconception. Parallelism can add speed, but it is neither the primary mechanism nor a requirement for vectorization to outperform loops. The profiler showing single-core usage is entirely consistent with a correct result.
B describes something that doesn't happen. R's vectorized functions don't skip elements or infer repeated values — they process every element. This confuses vectorization with something like run-length encoding or lazy evaluation.
D is subtly wrong because it attributes the explicit loop's slowness to single-core execution specifically, implying the loop would be competitive if parallelized. The real bottleneck is interpreter overhead, not core count.
As a study tip: whenever R performance questions mention "single core," don't assume that rules out speed gains — interpreted overhead, not parallelism, is usually the culprit.