diff --git a/previews/PR82/.documenter-siteinfo.json b/previews/PR82/.documenter-siteinfo.json index dd81b70..23eeaf4 100644 --- a/previews/PR82/.documenter-siteinfo.json +++ b/previews/PR82/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-09-16T20:06:22","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-10-08T04:18:36","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/previews/PR82/api/index.html b/previews/PR82/api/index.html index fe8bff9..cccfd62 100644 --- a/previews/PR82/api/index.html +++ b/previews/PR82/api/index.html @@ -6,7 +6,7 @@ foo (generic function with 1 method) julia> allocs = check_allocs(foo, (Int, Int)) -AllocCheck.AllocationSite[]source
AllocCheck.@check_allocsMacro
@check_allocs ignore_throw=true (function def)

Wraps the provided function definition so that all calls to it will be automatically checked for allocations.

If the check fails, an AllocCheckFailure exception is thrown containing the detailed failures, including the backtrace for each defect.

Note: All calls to the wrapped function are effectively a dynamic dispatch, which means they are type-unstable and may allocate memory at function entry. @check_allocs only guarantees the absence of allocations after the function has started running.

Example

julia> @check_allocs multiply(x,y) = x*y
+AllocCheck.AllocationSite[]
source
AllocCheck.@check_allocsMacro
@check_allocs ignore_throw=true (function def)

Wraps the provided function definition so that all calls to it will be automatically checked for allocations.

If the check fails, an AllocCheckFailure exception is thrown containing the detailed failures, including the backtrace for each defect.

Note: All calls to the wrapped function are effectively a dynamic dispatch, which means they are type-unstable and may allocate memory at function entry. @check_allocs only guarantees the absence of allocations after the function has started running.

Example

julia> @check_allocs multiply(x,y) = x*y
 multiply (generic function with 1 method)
 
 julia> multiply(1.5, 3.5) # no allocations for Float64
@@ -21,4 +21,4 @@
  [2] multiply(x::Matrix{Float64}, y::Matrix{Float64})
    @ Main ./REPL[2]:133
  [3] top-level scope
-   @ REPL[5]:1
source
+ @ REPL[5]:1source diff --git a/previews/PR82/index.html b/previews/PR82/index.html index 4e9e8a2..f6fdfd4 100644 --- a/previews/PR82/index.html +++ b/previews/PR82/index.html @@ -41,4 +41,4 @@ @test mysin1(1.5) == sin(1.5) @test_throws AllocCheckFailure mysin2(1.5)
Test Passed
-      Thrown: AllocCheckFailure

Limitations

Every call into a @check_allocs function behaves like a dynamic dispatch. This means that it can trigger compilation dynamically (involving lots of allocation), and even when the function has already been compiled, a small amount of allocation is still expected on function entry.

For most applications, the solution is to use @check_allocs to wrap your top-level entry point or your main application loop, in which case those applications are only incurred once. @check_allocs will guarantee that no dynamic compilation or allocation occurs once your function has started running.

+ Thrown: AllocCheckFailure

Limitations

Every call into a @check_allocs function behaves like a dynamic dispatch. This means that it can trigger compilation dynamically (involving lots of allocation), and even when the function has already been compiled, a small amount of allocation is still expected on function entry.

For most applications, the solution is to use @check_allocs to wrap your top-level entry point or your main application loop, in which case those applications are only incurred once. @check_allocs will guarantee that no dynamic compilation or allocation occurs once your function has started running.

diff --git a/previews/PR82/tutorials/error_recovery/index.html b/previews/PR82/tutorials/error_recovery/index.html index d00b7f5..ae1cf32 100644 --- a/previews/PR82/tutorials/error_recovery/index.html +++ b/previews/PR82/tutorials/error_recovery/index.html @@ -19,4 +19,4 @@ allocs = check_allocs(treading_lightly, ()) # Check that it's safe to proceed
Any[]
@test isempty(allocs)
Test Passed

check_allocs returned zero allocations. If we invoke check_allocs with the flag ignore_throw = false, we will see that the function may allocate memory on the error path:

allocs = check_allocs(treading_lightly, (); ignore_throw = false)
 length(allocs)
6

Finally, we test that the function is producing the expected result:

val = treading_lightly()
Test Passed

In this example, we accepted an allocation on the exception path with the motivation that it occurred once only, after which the program was terminated. Implicit in this approach is an assumption that the exception path does not allocate too much memory to execute the error recovery logic before the garbage collector is turned back on. We should thus convince ourselves that this assumption is valid, e.g., by means of testing:

treading_lightly() # Warm start
 allocated_memory = @allocated treading_lightly() # A call that triggers the exception path
-# @test allocated_memory < 1e4
205440

The allocations sites reported with the flag ignore_throw = false may be used as a guide as to what to test.

+# @test allocated_memory < 1e4
205440

The allocations sites reported with the flag ignore_throw = false may be used as a guide as to what to test.

diff --git a/previews/PR82/tutorials/hot_loop/index.html b/previews/PR82/tutorials/hot_loop/index.html index 05e12c8..3118066 100644 --- a/previews/PR82/tutorials/hot_loop/index.html +++ b/previews/PR82/tutorials/hot_loop/index.html @@ -55,4 +55,4 @@ workspace = setup() run!(workspace) end

Here, workspace is a custom struct designed to serve as a workspace for the hot loop, but it could also be realized as a simple tuple of all the allocated objects required for the computations. Note, the struct Workspace in this example was not marked as mutable. However, its contents, the two cache arrays, are. This means that the run! function may modify the contents of the cache arrays.

The benefit of breaking the function up into two parts which are called from a third, is that we may now create the workspace object individually, and use it to compute the type of the arguments to the run! function that we are interested in analyzing:

workspace = setup()
-allocs = check_allocs(run!, (typeof(workspace),))
+allocs = check_allocs(run!, (typeof(workspace),)) diff --git a/previews/PR82/tutorials/optional_debugging_and_logging/index.html b/previews/PR82/tutorials/optional_debugging_and_logging/index.html index 860f399..c27f481 100644 --- a/previews/PR82/tutorials/optional_debugging_and_logging/index.html +++ b/previews/PR82/tutorials/optional_debugging_and_logging/index.html @@ -25,4 +25,4 @@ return a end -constant_myfun()
6.0

When looking at constant_myfun, the compiler knows that verbose = false since this constant is hard coded into the program. Sometimes, the compiler can even propagate constant values all the way into called functions.

This is useful, but it's not guaranteed to happen in general. The Val{T} trick described here ensures that the variable is propagated as a constant everywhere it is required.

+constant_myfun()
6.0

When looking at constant_myfun, the compiler knows that verbose = false since this constant is hard coded into the program. Sometimes, the compiler can even propagate constant values all the way into called functions.

This is useful, but it's not guaranteed to happen in general. The Val{T} trick described here ensures that the variable is propagated as a constant everywhere it is required.