Problem Statement
Engineers routinely ask whether the LabVIEW timing primitives Wait (ms) and Wait Until Next ms Multiple can accept or resolve sub-millisecond intervals — microseconds or nanoseconds — for tight control or acquisition loops.
Short answer: no. Both functions take a millisecond-unit integer input. There is no unit selector, no fractional input, and no documented sub-millisecond mode. Wiring a smaller number does not buy finer resolution; it only floors to the nearest whole millisecond, and 0 simply yields a cooperative yield rather than a defined delay.
Root Cause: Where the Resolution Actually Comes From
Three separate mechanisms stack up, and each one imposes its own floor:
- Function API unit. The primitives are defined in milliseconds. That alone caps commanded resolution at 1 ms.
- OS timer granularity. A delay implemented on top of an operating-system sleep resolves to the system timer tick. Field reports on older Windows-based LabVIEW installations put that effective granularity in the tens of milliseconds — on the order of 50 ms in the worst-reported cases. Later LabVIEW releases (from LabVIEW 7 onward) added a higher-resolution timer path that is generally trustworthy down into the millisecond range. Verify the behavior for your exact LabVIEW version and OS rather than assuming.
- Preemption and jitter. Even with a 1 ms-capable timer, a higher-priority thread, a driver interrupt, disk I/O, or a GUI redraw can hold the loop off for tens of milliseconds. This is unbounded on a non-real-time OS.
Wait (ms) vs Wait Until Next ms Multiple
| Aspect | Wait (ms) | Wait Until Next ms Multiple |
|---|---|---|
| Semantics | Delay at least N ms from when the node starts | Block until the ms timer value is an integer multiple of N |
| Accumulated drift | Drifts — loop period = code time + N | Self-correcting against the ms timer; no long-term drift |
| First iteration | Full N ms | Partial (0 to N ms) — phase-dependent |
| Best use | Simple throttling, non-periodic pacing | Periodic loops where average rate must hold |
| Sub-ms capable | No | No |
If code inside the loop exceeds N ms, Wait Until Next ms Multiple does not queue up missed ticks — it waits for the next multiple, so you silently lose iterations. That is a common cause of a "10 ms" loop running at 20 ms with no error indication.
Solution Paths for Sub-Millisecond Requirements
Pick the path by how tight the requirement is and whether the timing must be deterministic (bounded worst case) or merely fast on average.
| Requirement | Approach | Notes |
|---|---|---|
| > 50 ms period, loose | Wait Until Next ms Multiple on desktop OS | Adequate for HMI updates, file logging, supervisory polling |
| 1–50 ms period, some jitter tolerated | High-resolution millisecond timer path, loop priority raised, non-critical UI moved to a separate loop | Measure jitter; do not assume |
| 1–10 ms period, bounded jitter | Real-time OS target with a timed loop construct | Determinism comes from the RTOS scheduler, not from the Wait primitive |
| < 1 ms, or hard determinism | Move timing into hardware | Hardware clock/counter, DAQ sample clock, or FPGA logic |
Hardware-Timed Acquisition Is the Correct Answer Below 1 ms
For sampling, do not pace acquisition with a software wait at all. Configure the acquisition device for continuous, hardware-clocked sampling and let the software loop simply read blocks from the buffer. The sample instants are then defined by the device oscillator, and software jitter only affects when you drain the buffer, not when samples were taken. Size the buffer so the worst-case software stall (tens to hundreds of milliseconds on a desktop OS) cannot overrun it:
buffer_samples >= sample_rate_Hz * worst_case_stall_s * safety_factor
Example (labeled assumption: 100 kS/s, 250 ms worst-case stall, x4 margin):
100000 * 0.250 * 4 = 100000 samples per channel
For output pulses, edge generation, or closed-loop control faster than 1 kHz, generate the waveform or control law on a counter/timer, a hardware waveform generator, or an FPGA target. Software then only writes setpoints.
Measuring What You Actually Get
Never publish a loop rate you have not measured. Instrument the loop before trusting it:
- Build a minimal loop containing only the timing primitive under test and a timestamp read. Use the highest-resolution tick source your LabVIEW version exposes.
- Capture at least 10,000 consecutive iteration timestamps into a preallocated array. Do not write to disk, update a chart, or use the front panel inside the loop — each of those adds its own stall.
- Compute the first difference to get per-iteration periods. Report min, max, mean, standard deviation, and the 99.9th percentile. The max is the number that matters for control.
- Repeat the run while the machine is loaded: open a browser, run a file copy, force a virus scan. The delta between idle and loaded max is your real jitter budget.
- Cross-check with an external instrument. Toggle a digital output each iteration and measure the pulse train on a scope or frequency counter. Software measuring itself shares the same clock and the same preemption.
Practical Loop Design Rules
- Separate the loops. Keep the time-critical loop free of front-panel terminals, property nodes, dialogs, and file I/O. Pass data to a display loop through a queue or RT FIFO.
- Preallocate. Array building and string concatenation inside the loop trigger memory allocation, which is a non-deterministic stall. Initialize arrays to final size and replace elements.
- Never busy-wait. Polling a tick count in a tight loop to fake sub-millisecond delay burns 100% of a core, starves other threads, and still does not give determinism. It makes system-wide jitter worse.
-
Watch the first iteration. With
Wait Until Next ms Multiple, the first wait is a partial interval. Discard the first sample or the first few periods from any timing statistic. - Detect missed ticks. Compare the measured iteration period against the target inside the loop and increment a fault counter when it exceeds tolerance. A silently slow loop is worse than an error.
- Check the LabVIEW Help entry for the specific timing function and for the software-timing topics in your installed version. Behavior of the timer source has changed across releases, and the Help is version-matched to what you are actually running.
Decision Summary
| Question | If yes | If no |
|---|---|---|
| Is the period > 50 ms and jitter tolerant? | Software wait on desktop is acceptable | Continue |
| Is it acquisition or waveform output? | Use hardware clocking; software only moves buffers | Continue |
| Is a bounded worst case required? | Real-time target or FPGA | Desktop loop with measured jitter budget and fault counting |
| Is the period < 1 ms? | Hardware/FPGA only | Software may work — measure to confirm |
Can Wait (ms) accept microseconds or nanoseconds in LabVIEW?
No. Both Wait (ms) and Wait Until Next ms Multiple take a millisecond-unit integer with no sub-millisecond mode. Sub-millisecond timing must come from hardware clocking or FPGA logic.
Why does my 1 ms LabVIEW loop actually run at 15–50 ms?
The delay is implemented over the OS timer, and on older Windows-based setups that granularity was reported in the tens of milliseconds — around 50 ms in bad cases. Any higher-priority thread or interrupt adds further unbounded delay on a non-real-time OS.
What is the difference between Wait (ms) and Wait Until Next ms Multiple?
Wait (ms) delays at least N ms after the node starts, so loop period drifts with code execution time. Wait Until Next ms Multiple blocks until the millisecond timer hits a multiple of N, which self-corrects drift but silently skips iterations if the loop body overruns N.
How do I get deterministic sub-millisecond timing?
Move the timing out of software: use a hardware sample clock on the acquisition device, a counter/timer for pulse generation, or implement the loop on an FPGA target. Software then only reads and writes buffers.
How do I verify the real jitter of my timed loop?
Log 10,000+ iteration timestamps to a preallocated array with no UI or file I/O in the loop, then report min, max, mean, and 99.9th percentile of the first difference. Repeat under heavy system load and cross-check with a scope on a toggled digital output.