问题
I have the following version of isPrime
written (and proved) in Coq.
- It takes around
30
seconds forCompute (isPrime 330)
to finish on my machine. - The extracted Haskell code takes around
1
second to verify that9767
is prime.
According to a comment in this post, the timing difference means nothing, but I wonder why is that? and is there any other way to predict performance when extracting Coq code? after all, sometimes performance does matter, and it's quite hard to change Coq source once you labored to prove it's correct. Here is my Coq code:
(***********)
(* IMPORTS *)
(***********)
Require Import Coq.Arith.PeanoNat.
(************)
(* helper'' *)
(************)
Fixpoint helper' (p m n : nat) : bool :=
match m with
| 0 => false
| 1 => false
| S m' => (orb ((mult m n) =? p) (helper' p m' n))
end.
(**********)
(* helper *)
(**********)
Fixpoint helper (p m : nat) : bool :=
match m with
| 0 => false
| S m' => (orb ((mult m m) =? p) (orb (helper' p m' m) (helper p m')))
end.
(***********)
(* isPrime *)
(***********)
Fixpoint isPrime (p : nat) : bool :=
match p with
| 0 => false
| 1 => false
| S p' => (negb (helper p p'))
end.
(***********************)
(* Compute isPrime 330 *)
(***********************)
Compute (isPrime 330).
(********************************)
(* Extraction Language: Haskell *)
(********************************)
Extraction Language Haskell.
(***************************)
(* Use Haskell basic types *)
(***************************)
Require Import ExtrHaskellBasic.
(****************************************)
(* Use Haskell support for Nat handling *)
(****************************************)
Require Import ExtrHaskellNatNum.
Extract Inductive Datatypes.nat => "Prelude.Integer" ["0" "succ"]
"(\fO fS n -> if n Prelude.== 0 then fO () else fS (n Prelude.- 1))".
(***************************)
(* Extract to Haskell file *)
(***************************)
Extraction "/home/oren/GIT/CoqIt/FOLDER_2_PRESENTATION/FOLDER_2_EXAMPLES/EXAMPLE_03_PrintPrimes_Performance_Haskell.hs" isPrime.
回答1:
Your Coq code is using a Peano encoding of the naturals. The evaluation of mult 2 2
literally proceeds by the reduction:
mult (S (S 0)) (S (S 0)))
= (S (S 0)) + mult (S 0) (S (S 0)))
= (S (S 0)) + ((S (S 0)) + mult 0 (S (S 0)))
= (S (S 0)) + ((S (S 0)) + 0)
= (S (S 0)) + ((S 0) + (S 0))
= (S (S 0)) + (0 + (S (S 0))
= (S (S 0)) + (S (S 0))
= (S 0) + (S (S (S 0)))
= 0 + (S (S (S (S 0)))
= (S (S (S (S 0))))
and then checking the equality mult 2 2 =? 5
proceeds by the further reduction:
(S (S (S (S 0)))) =? (S (S (S (S (S 0)))))
(S (S (S 0))) =? (S (S (S (S 0))))
(S (S 0)) =? (S (S (S 0)))
(S 0) =? (S (S 0))
0 =? (S 0)
false
Meanwhile, on the Haskell side, the evaluation of 2 * 2 == 5
proceeds by multiplying two Integers
and comparing them to another Integer
. This is somewhat faster. ;)
What's incredible here is that Coq's evaluation of isPrime 330
only takes 30 seconds instead of, say, 30 years.
I don't know what to say about predicting the speed of extracted code, except to say that primitive operations on Peano numbers will be massively accelerated, and other code will probably be modestly faster, simply because a lot of work has gone into making GHC generate fast code, and performance hasn't been an emphasis in Coq's development.
来源:https://stackoverflow.com/questions/55796487/predict-running-times-of-extracted-coq-code-to-haskell