/ / Ist es möglich, in Julia Plausibilitätsprüfungen zu organisieren, damit sie beim Laden eines Pakets leicht kompiliert werden können? - Testen, Julia-lang, Assertieren, Flags, Code-Organisation

Ist es möglich, in Julia Sanitätsprüfungen zu organisieren, damit sie beim Laden eines Pakets einfach kompiliert werden können? - Testen, Julia-Lang, Assert, Flags, Code-Organisation

Ich entwickle ein Paket, das schnell laufen mussund richtig sein. Ich möchte nur eine Funktion schreiben, habe aber zwei "Versionen" dieser Funktion: eine, die sofort stoppt, wenn sie irgendein komisches Geschäft entdeckt, und eine andere, die einfach so schnell wie möglich läuft. Meine Idee ist, die strikte Version der Funktion für eine zufällige Stichprobe von Eingaben auszuführen, und vorausgesetzt, dass nichts fehlschlägt, führen Sie die schnelle Version für den gesamten Satz von Eingaben aus. Ich habe definitiv nicht die Rechenleistung, um jede Prüfung bei jeder Eingabe durchzuführen, obwohl ich besser schlafen würde, wenn ich könnte. (Ich bin offen dafür, dass dies bereits der falsche Weg ist, das Verifikationsproblem anzugehen, also wenn Wenn Sie einen besseren Organisationsansatz haben, können Sie dies gerne anstelle einer spezifischen Verbesserung unten vorschlagen.)

Bisher habe ich eine temporäre Lösung für dieses Problem wie folgt gehackt:

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

Das funktioniert also,

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

Und wenn ich das Modul neu lade, in dem ich manuell geändert habe const using_removeable_assertions = false, dann überspringt es nicht nur diese Überprüfungen,

julia> some_function(1.0,1.0)
1.5

julia> some_function(1.0,-1.0)
0.5

Tatsächlich ist der native Code jedoch wie beabsichtigt mit derselben Funktion identisch, bei der ich diese Zeilen nie eingefügt habe:

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

Z.B. wenn ich definiere

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

Dann

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

Also: das sind zwei der gewünschten Eigenschaften einer Lösung,

  1. Das Ein- und Ausschalten einer großen Reihe strenger Gesundheitsprüfungen sollte so einfach sein wie das Umlegen eines Schalters
  2. Im Fall "off" sollte der native Code vollständig frei von Verweisen auf die Prüfungen sein; insbesondere sollte es keine Leistungsunterschied im Off-Fall

aber ich hätte gerne eine Lösung, die etwas weniger manuell und etwas idiomatischer oder standardisierter ist.

Beachten Sie, dass es unpraktisch ist, zwei Versionen jeder Funktion manuell zu definieren, da deren Wartung ein Durcheinander wäre. Also habe ich darüber nachgedacht, ein anderes Makro zu verwenden, das eine some_function_safe für jede some_function und dann einfach die sicheren Überprüfungen manuell auf diese Weise ausführen, aber ich hoffe, es gibt eine bessere Lösung, so etwas wie ein Befehlszeilenargument zu julia, aber modulspezifisch. Z.B. using[safemode=yes] Foo.

In diesem Zusammenhang frage ich mich, ob es einen guten Ansatz gibt, umUmgang mit mehreren Check-"Levels", über einen ähnlichen Hack hinaus, z.B. Definieren eines globalen "Sicherheitsniveaus" und anschließendes Verwenden von Makros, um bedingt Prüfungen einzufügen, vorausgesetzt, das globale Sicherheitsniveau überschreitet das spezifische Prüfniveau. Wenn die [safemode=yes] obige Idee könnte umgesetzt werden, dann könnte dies sicherlich auch umgesetzt werden, aber ich frage mich, ob es einfach einen besseren Weg gibt, diese Art von Überprüfungen zu organisieren.

(FWIW, ich betrachte diese Kontrollen als Ergänzungen zu,kein Ersatz für eine große Testsuite. Die Testsuite allein lässt mich nicht einschlafen. Auch wenn mein Beispiel die Argumentüberprüfung ist, möchte ich diese Prüfungen nicht nur für Vor- und Nachbedingungen verwenden, sondern auch für alle Arten von Plausibilitätsprüfungen, z Methodenkörper und Schreiben in spezifischen Prüfungen auf Fehler, die ich bereits gemacht und behoben habe, etc.)

Antworten:

1 für die Antwort № 1

Ich mache etwas ziemlich Ähnliches für meine Julia-Arbeit,Ich habe ein Makro, das entweder vollständig entfernt werden kann oder, wenn es einkompiliert wird, eine Maske überprüft, die ich festlegen kann, um zu steuern, welche tatsächlich überprüft werden. Was Unit-Tests angeht, haben wir die FactCheck.jl Paket.Wir planen, Code einzurichten, um Code-Coverage-Tests durchzuführen (wir können unseren internen Code nicht auf GitHub ablegen, wo es trivial wäre, ihn einzurichten). . Wenn sich Ihr Code auf GitHub befindet, denke ich, dass das Hinzufügen von Codeabdeckungsunterstützung entweder von Overalls oder codecov.io trivial ist.