Repository navigation
Conversation
It does? I'm pretty sure to_ref is just a wrapper around std::forward, no?
I don't think we need to jump through hoops to keep errors in the same order |
Jenkins Console Log Machine informationDistributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal CPU: Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian Address sizes: 43 bits physical, 48 bits virtual CPU(s): 256 On-line CPU(s) list: 0-255 Thread(s) per core: 2 Core(s) per socket: 64 Socket(s): 2 NUMA node(s): 2 Vendor ID: AuthenticAMD CPU family: 23 Model: 49 Model name: AMD EPYC 7742 64-Core Processor Stepping: 0 Frequency boost: enabled CPU MHz: 1496.827 CPU max MHz: 3416.0681 CPU min MHz: 1500.0000 BogoMIPS: 4491.56 Virtualization: AMD-V L1d cache: 4 MiB L1i cache: 4 MiB L2 cache: 64 MiB L3 cache: 512 MiB NUMA node0 CPU(s): 0-63,128-191 NUMA node1 CPU(s): 64-127,192-255 Vulnerability Gather data sampling: Not affected Vulnerability Indirect target selection: Not affected Vulnerability Itlb multihit: Not affected Vulnerability L1tf: Not affected Vulnerability Mds: Not affected Vulnerability Meltdown: Not affected Vulnerability Mmio stale data: Not affected Vulnerability Old microcode: Not affected Vulnerability Reg file data sampling: Not affected Vulnerability Retbleed: Mitigation; untrained return thunk; SMT enabled with STIBP protection Vulnerability Spec rstack overflow: Mitigation; Safe RET Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization Vulnerability Spectre v2: Mitigation; Retpolines; IBPB conditional; STIBP always-on; RSB filling; PBRSB-eIBRS Not affected; BHI Not affected Vulnerability Srbds: Not affected Vulnerability Tsa: Not affected Vulnerability Tsx async abort: Not affected Vulnerability Vmscape: Mitigation; IBPB before exit to userspace Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid extd_apicid aperfmperf rapl pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate ssbd mba ibrs ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd amd_ppin arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif v_spec_ctrl umip rdpid overflow_recov succor smca sev sev_es G++: g++ (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0 Copyright (C) 2019 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Clang: clang version 10.0.0-4ubuntu1 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /usr/bin |
I have to admit that I'm one edge of my knowledge. This is what Claude says:
Agreed, I removed that code in dde60be194. |
In reverse mode with vector arguments, with this PR
normal_lpdftakes 0.17 to 0.99 of thedeveloptime in the cases that I measured (see Speed). Values and gradients agree withdevelopup to rounding. Exceptions have the same types and texts as ondevelop, unlesssigmaandyormuare both invalid; then the text is aboutsigma.The same speedup could be made for other distributions, but I start with this one to make this easier to review. Some of the ideas have already been used in
normal_id_glm_lpdfand with this kind of speedup + O1, we would get most of the benefits of hand written glm functions without need to actually add them to Stan math and language (I have done those speed experiments with other distributions, but not showing the timings here as this is about normal distribution).The scripts behind the numbers in this text are in https://github.com/avehtari/stan-pr-checks/tree/724274d2d3ea10b1e2ce11d08ac71d941118b14d/pr3498. The
README.mdthere gives the commands and the expected output for each claim (sections 1 to 7). Section 8 shows theto_refcopy mentioned in the Summary.This PR was Claude-assisted.
Summary
On
develop, two parts of a vectorized call cost 56 to 76 % of its time in case A below (measured with a prototype):check_not_nan(y)andcheck_finite(mu)run before the arithmetic. Each is a loop with a test per element.y_scaledis a heap array of N doubles. So isy_scaled_sqwhensigmais an autodiff argument, andscaled_diffwhen two or more arguments are autodiff arguments. In the prototype, removingy_scaled_sqorscaled_diffsaved 1.7 to 4.4 ns per element from N = 1e4.to_refalso copies the values of aMatrix<var>argument into a heap array: its return typeref_type_tis a plain array whenEigen::Refcannot bind the value view.The changes:
sigmafirst, right aftercheck_consistent_sizes.yandmurun only if the sum of squares is not finite. A NaNyor a non-finitemualways makes the sum NaN or infinite, also whensigmais+inf. An infiniteywith a finitemupasses the checks and gives-inf, as ondevelop.normal_id_glm_lpdfhas used this approach since Glm optimizations #1185.yandmustill run before the size-zero return and before the return for all-data arguments withpropto = true.muis an autodiff vector, its partials first hold(y - mu) / sigma; otherwise the partials of an autodiffyvector do. The function takes the sum of squares from them and then scales them in place. The partial ofyis the negated partial ofmu.sigma, its partials first hold1 / sigma. Ifmuoryis also an autodiff vector, one loop then writes both partials; else an expression writes the partials ofsigmain place.sigmapartial isinv_sigma * (sum_sq - N), not a sum over N elements.Apart from the scalar
sigmapartial, the value and the partials use thedevelopformulas.Speed
Triton, Xeon Gold 6248, g++ 14.3.0,
-O3, one core. One evaluation creates the autodiff arguments, callsnormal_lpdf, runsgrad()andrecover_memory(). Each case and version is a separate binary; thedevelopand branch binaries alternate over 5 rounds. Each cell is the time of the branch divided by the time ofdevelop(median over the rounds). D is data, V is an autodiff argument, varmat isvar_value<VectorXd>.MatrixMatrixMatrixMatrixMatrixMatrixMatrixstd::vectorstd::vectorMatrixIn case A,
developtakes 4.6 ns per element at N = 100 and 11 to 15 ns from N = 1e4. The branch takes 1.9 to 4.0 ns. The largest ratio of a single round in the table is 0.89 (case C, N = 1e3).The smallest speedup I measured is for a data vector
sigmawith scalar datayand an autodiffmuvector: 0.96 to 0.99 at N <= 1e3 and 0.62 to 0.72 from N = 1e4. There the branch computes1 / sigmatwice per element, wheredevelopcomputes it once into a heap array.Testing
A check program compares the branch with a copy of the
developfunction in the same binary. It covers:Matrix<var>, rowMatrix<var>, varmat column and row vectors,std::vector<var>, scalars;sigmatogether with invalidyormu, size zero, size mismatch;proptotrue and false, reverse mode andfvar<double>.In 1512 of the 1692 comparisons, the branch gives the same exception text as
develop, or values and gradients within 1e-12 times max(1, |reference|). The largest error is 2.4e-15 for the values and 1.4e-15 for the gradients. The other 180 comparisons have an invalidsigmaand an invalidyormu, and the branch reportssigma. Amalloccounter finds no allocation of 8 N bytes or more on the branch in any of the 25 type cases;developmakes up to 7 such allocations per evaluation.New tests in
test/unit/math/prim/prob/normal_test.cppandtest/unit/math/mix/prob/normal_test.cpp:Matrix<var>, varmat,std::vector<var>,fvar);y, and size zero with an invalid scalar;Matrix<var>, rowMatrix<var>, varmat (column and row) andstd::vector<var>;expect_adwith vector arguments.The new tests also pass with the
developheader, since no call has more than one invalid argument. To check that they cover the new code, I removed, in turn, the check after the sum of squares in each of the two branches of the arithmetic (one for an autodiffmuoryvector, one for the other cases). At760f6a1f65, 8 and 5 assertions failed.These tests pass:
prim/prob/normal_test,mix/prob/normal_test,rev/prob/normal_log_test,rev/prob/normal_id_glm_lpdf_testand the generatedtest/prob/normallpdf tests.rev/prob/normal_id_glm_lpdf_testusesnormal_lpdfas reference.opencl/rev/normal_lpdf_testpasses on a V100. It compares the unchanged OpenCL function with the changed CPU function.cpplint,
make test-math-dependencies, the header check,make test-headersand clang-format 10 pass.make doxygenwith doxygen 1.9.7 gives no warnings.The unit tests, the generated tests, the OpenCL test and the checklist items ran at
421386dec5(the change applied todevelopatdc66941969) with g++ 14.3.0.With
fvar<var>arguments and an invalidyormu, the function now throws after it has made var nodes; ondevelopit threw before. The nodes stay on the stack untilrecover_memory().Related PRs and issues
normal_lpdf.hpp. Cleanup: Remove unused #includes in stan/math and tests (~minus 3kLOC) #3470 removes three includes there; this PR changes the include block too, so the two conflict in that block only.make_partials_propagatorto return its input args as a tuple #3423 proposes a new partials interface and usesnormal_lpdfas its example. This PR uses the presentpartials_propagator.check_foreach(...)function that does multiple checks at once on an array or matrix #3417 proposes one pass over an argument for several checks. This PR avoids the pass overyandmuwhen the inputs are valid.Release notes
Vectorized
normal_lpdfis faster: it no longer allocates heap arrays of size N, and it checksyandmuonly when the sum of squares is not finite.Checklist
Copyright holder: Aalto University
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
./runTests.py test/unit)make test-headers)make test-math-dependencies)make doxygen)make cpplint)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested