Dev Docs Performance Tuning
Performance Tuning
Audited against Netatalk
d63155e13on 2026-10-10. This describes the current source checkout; older releases may differ.
Scope and measurement
This page documents performance controls implemented in the current Netatalk source. Results depend on clients, concurrency, filesystem, and platform. Link speed alone does not determine buffer sizes, cache budgets, or database choice.
The earlier page mixed obsolete Netatalk defaults with community kernel, macOS, and ZFS recipes and unsupported performance estimates. Those recipes are not maintained here as source-code facts. Use the relevant operating system’s documentation for its tuning controls, and measure the deployed workload before changing defaults.
Current controls
Defaults come from afp_config_parse() in libatalk/util/netatalk_conf.c; constants are in include/atalk/globals.h, include/atalk/dsi.h, and include/atalk/netatalk_conf.h.
[Global] option |
Default | What it controls |
|---|---|---|
server quantum |
1,048,576 bytes (1 MiB) | Advertised maximum DSI write-data payload |
dsireadbuf |
32 | Incoming DSI readahead allocation multiplier |
tcpsndbuf, tcprcvbuf |
0 | Leave socket buffers to OS; nonzero requests SO_SNDBUF/SO_RCVBUF |
dircache size |
65,536 | Live directory/file cache capacity per worker |
dircache mode |
arc |
Adaptive replacement; lru also supported |
dircache validation freq |
100 | Sample filesystem validation every Nth eligible lookup |
dircache rfork budget |
32,768 KiB | Resource-fork content budget per worker; 0 disables |
dircache rfork maxsize |
1,024 KiB | Largest fork admitted to the content cache |
use sendfile |
yes | Permit compiled sendfile paths for eligible reads |
recvfile |
no | Permit compiled splice/recvfile path for eligible writes |
splice size |
1,048,576 bytes | Requested recvfile pipe capacity |
strict locking |
no | Temporary POSIX locks around data I/O |
spotlight results limit |
10,000 | Search result/candidate bound; 0 is unlimited |
multi protocol = yes changes default validation to 1 and default strict locking to yes, and excludes that volume from resource-fork caching unless the budget was explicitly set. These effects must be accounted for when comparing configurations.
DSI buffering
server quantum is a protocol limit on write data, not a filesystem block size or a universal maximum reply size. The configured range is 32,000 bytes to 256 MiB. libatalk/dsi/dsi_tcp.c can align a session quantum to TCP MSS within bounded growth, accounting for DSI/AFP write-frame overhead.
dsireadbuf allocates incoming network readahead in libatalk/dsi/dsi_init.c and libatalk/dsi/dsi_tcp.c. The configured multiplier is bounded to 6–1,024, then reduced if necessary so the configured product fits NETATALK_READBUF_LIMIT (1 GiB) and address-space limits. This product limit can reduce the multiplier below 6 for a large quantum.
Increasing readahead can help when dsi_peek() repeatedly fills its buffer, but consumes memory per session. It does not prefetch file data from disk. There is no code requirement that a 10 GbE connection allocate at least 128 MiB, nor that a ZFS record size match the quantum.
afp_over_dsi() applies explicit socket-buffer requests and logs configured and actual values using getsockopt(). The kernel can adjust or reject requests. Inspect those values and observed transfer behavior instead of assuming a requested size equals allocated kernel memory.
Memory accounting
A useful planning calculation for the default receive-side user buffers is:
command buffer: 1 MiB
DSI readahead buffer: 32 × 1 MiB = 32 MiB
subtotal: approximately 33 MiB per DSI worker
This is an allocation estimate before session alignment and other overhead. It is not total RSS. Add reply buffers, the directory cache and ARC ghosts, names/paths, AD metadata, resource-fork buffers actually populated, database state, open forks, thread stacks, and kernel socket/pipe memory. Heap allocations are not all necessarily resident at once.
ARC retains full struct dir ghost entries, so memory use can exceed a calculation based only on the live-entry count. The resource-fork budget is a separate limit, not memory automatically consumed at startup. Multiply measured process memory by the number of workers and include disconnected/sleeping sessions retained for reconnect.
Cache and validation tradeoffs
Use Caching Architecture for algorithms, validity checks, and diagnostics. A lower validation frequency value performs more stat calls; a higher value allows longer stale windows for changes outside AFP. Internal ordinary hints are best effort and do not replace validation.
The 16-slot parent-directory fd cache accelerates validation with relative stat operations; PFD_REGROUND_INTERVAL is 256 uses. It has its own convergence behavior under external parent-directory replacement. See etc/afpd/pfd_cache.c and test/afpd/subtests_pfd.c.
Cache hit rates depend on workload and warm-up. A universal 90% target or claimed 80–95% of line-rate throughput is not an implementation guarantee.
Sendfile and recvfile
Sendfile depends on the platform, selected fork storage, and build feature detection. It is not synonymous with all outgoing data avoiding copies. Implementation is in libatalk/adouble/ad_sendfile.c and libatalk/dsi/dsi_read.c.
Recvfile depends on splice support and recvfile = yes. libatalk/adouble/ad_recvfile.c lazily creates a per-session pipe, requests capacity where supported, and records the capacity the kernel grants. splice size is bounded from 4 KiB to 8 MiB and rounded to a page boundary by the parser. A denied pipe resize falls back to usable capacity; it does not justify documenting the requested value as guaranteed.
Failure paths maintain DSI frame alignment and map file-size/write errors appropriately. Relevant regression coverage is in test/afpd/subtests_recvfile.c. Measure CPU cost and transfer rate with the option off and on for eligible writes.
Metadata, database, and search workload
SQLite is mandatory/default and uses WAL with a 3-second busy timeout. MySQL is optional. Neither is universally fastest; distinguish query latency, directory traversal, write contention, and cache warm-up. There are no BDB cache/checkpoint controls in this checkout.
Spotlight backend choice changes what is indexed and where work runs. CNID name searches, Localsearch’s external index, and Xapian’s embedded index are different workloads. spotlight results limit applies across backends. See CNID Database and Data Flow.
Repeatable evaluation
- Record source revision, build options, configuration, platform, storage format, client version, and session count.
- Separate bulk transfer, small-file creation, directory enumeration, metadata lookup, and search workloads.
- Compare cold and warm runs. Record throughput, latency, per-worker memory, CPU, I/O, contention, and cache statistics.
- Change one relevant option, repeat the same workload, and check correctness as well as speed.
- Include external modifications when evaluating
multi protocolvolumes and retained sessions when evaluating memory.
Build the bundled tools with -Dwith-testsuite=true. test/testsuite/lantest.c measures AFP workloads; test/testsuite/speedtest.c and test/testsuite/speedtest_local_vfs.c provide transfer/local VFS comparisons. Their current command-line usage and test/README.md are the reference for invocation. Diagnostics are described in Troubleshooting.
Footnotes
Generated from the Netatalk GitHub Wiki
Last updated 2026-10-21