/ / Czy jest możliwe zorganizowanie kontroli zdrowia w Julii, aby ułatwić ich kompilację podczas ładowania pakietu? - testowanie, julia-lang, assert, flagi, organizacja kodu

Czy możliwe jest zorganizowanie kontroli zdrowia w Julii, aby ułatwić ich kompilację podczas ładowania pakietu? - testowanie, julia-lang, assert, flagi, organizacja kodu

Opracowuję pakiet, który musi działać szybkoi bądź poprawny. Chcę napisać tylko jedną funkcję, ale mam dwie „wersje” tej funkcji: jedną, która natychmiast zatrzymuje się, gdy wykryje jakąkolwiek zabawną firmę, a drugą, która działa tak szybko, jak to możliwe. Moim pomysłem jest uruchomienie ścisłej wersji funkcji na losowej próbce wejść i pod warunkiem, że nic się nie powiedzie, uruchom szybką wersję na całym zestawie wejść. Zdecydowanie nie mam mocy obliczeniowej, aby uruchamiać każde sprawdzenie każdego wejścia, nawet gdybym mógł spać łatwiej, gdybym mógł. masz lepsze podejście do organizacji, sugeruj, że zamiast określonego ulepszenia poniżej).

Do tej pory zaatakowałem tymczasowe rozwiązanie tego problemu w następujący sposób:

module Foo

export some_function

const using_removeable_assertions = true

macro RemoveablyAssert(the_condition)
if using_removeable_assertions
return esc(:($the_condition || error("Assertion failed.")))
else
return esc("")
end
end

function some_function(x::Float64,y::Float64)
#Say this should only be called on positive numbers
@RemoveablyAssert x>0
@RemoveablyAssert y>0
(x + y/2.0)::Float64
end

end

using Foo

Więc to działa,

julia> some_function(1.0,1.0)
1.5

julia> some_function(1.0,-1.0)
ERROR: Assertion failed.
in some_function at none:18

A jeśli przeładuję moduł, w którym ręcznie zmieniłem const using_removeable_assertions = false, to nie tylko pomija te kontrole,

julia> some_function(1.0,1.0)
1.5

julia> some_function(1.0,-1.0)
0.5

ale w rzeczywistości, zgodnie z przeznaczeniem, kod natywny jest identyczny z tą samą funkcją, w której nigdy nie umieściłem tych linii:

julia> @code_native some_function(1.0,1.0)
.section    __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 19
pushq   %rbp
movq    %rsp, %rbp
movabsq $13174486592, %rax      ## imm = 0x31142B640
Source line: 19
vmulsd  (%rax), %xmm1, %xmm1
vaddsd  %xmm0, %xmm1, %xmm0
popq    %rbp
ret

Na przykład. jeśli zdefiniuję

function some_function_2(x::Float64,y::Float64)
(x + y/2.0)::Float64
end

Następnie

julia> @code_native some_function_2(1.0,1.0)
.section    __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 2
pushq   %rbp
movq    %rsp, %rbp
movabsq $13174493536, %rax      ## imm = 0x31142D160
Source line: 2
vmulsd  (%rax), %xmm1, %xmm1
vaddsd  %xmm0, %xmm1, %xmm0
popq    %rbp
ret

Zatem: są to dwie pożądane właściwości rozwiązania,

  1. Włączanie / wyłączanie dużego zestawu rygorystycznych testów zdrowia powinno być tak proste, jak przełączanie przełącznika
  2. W przypadku „off” kod natywny powinien być całkowicie czysty od wszelkich odniesień do kontroli; w szczególności nie powinno być różnica wydajności w przypadku wyłączonym

ale chciałbym rozwiązanie nieco mniej ręczne i trochę bardziej idiomatyczne lub standardowe.

Zauważ, że niepraktyczne jest ręczne definiowanie dwóch wersji każdej funkcji, ponieważ ich utrzymywanie byłoby bałaganem. Więc pomyślałem o użyciu innego makra, które generuje some_function_safe dla każdego some_function a następnie po prostu ręcznie uruchamia bezpieczne sprawdzanie w ten sposób, ale mam nadzieję, że istnieje lepsze rozwiązanie, coś w rodzaju argumentu linii poleceń julia, ale specyficzne dla modułu. Na przykład. using[safemode=yes] Foo.

W związku z tym zastanawiam się, czy nie ma dobrego podejściaobsługa wielu poziomów „kontroli” poza podobnym hackiem, np. zdefiniowanie globalnego „poziomu bezpieczeństwa”, a następnie wykorzystanie makr do warunkowego wstrzykiwania kontroli pod warunkiem, że globalny poziom bezpieczeństwa przekracza określony poziom kontroli. Jeśli [safemode=yes] powyższy pomysł mógłby zostać zaimplementowany, to z pewnością można by go również wdrożyć, ale zastanawiam się, czy istnieje tylko lepszy sposób na organizowanie tego rodzaju kontroli.

(FWIW, uważam, że te kontrole uzupełniają,nie zastępuje dużego zestawu testów. Sam zestaw testów nie sprawia, że ​​śpię łatwo. Ponadto, chociaż moim przykładem jest weryfikacja argumentów, chcę używać tych kontroli nie tylko do warunków przed i po, ale także do wszelkiego rodzaju testów poprawności, np. Sprawdzania niezmienników ciał metod i pisania w określonych kontrolach pod kątem błędów, które już popełniłem i naprawiłem itp.)

Odpowiedzi:

1 dla odpowiedzi № 1

Robię coś dość podobnego do mojej pracy Julii,Mam makro, które można albo całkowicie usunąć, albo wkompilować, sprawdza maskę, którą mogę ustawić, aby kontrolować, które z nich są rzeczywiście sprawdzane. Jeśli chodzi o testy jednostkowe, użyliśmy FactCheck.jl pakiet. Planujemy skonfigurować kod do testowania pokrycia kodu (nie możemy umieścić naszego wewnętrznego kodu na GitHub, gdzie konfigurowanie byłoby trywialne), myślę, że jest to krytyczne, wraz z testami jednostkowymi, aby zapewnić rozsądnie dobry kod . Jeśli twój kod jest na GitHub, myślę, że dodanie obsługi pokrycia kodu z coveralls lub codecov.io jest banalne.