Git Product home page Git Product logo

eiffel-studio-contract-performance's Introduction

eiffel-studio-contract-performance

Demo of time comparison with contracts enabled and disabled

In this repository we compare the time performance of a project compiled with contracts enabled and disabled. Examples are compiled with EiffelStudio 19.5

SCOOP disabled. Void checking in runtime disabled.

Time comparison

Iterations: 200000000

Runs of the same program: 10

All times are in seconds

name avg min max
no_keep 0.581 0.577 0.584
all_enabled 6.055 6.016 6.097
only_target 2.673 2.648 2.707
only_cluster 6.497 6.036 7.47
all_disabled 2.895 2.805 3.291

All examples are compiled with -finalize option.

  • no_keep compiles the program without the -keep option. All other examples are compiled with -keep.
  • all_enabled has contracts enabled for both the target and the cluster
  • only_target has contracts enabled for the target, and disabled for the cluster
  • only_cluster has contracts enabled for the cluster, and disabled for the target
  • all_disabled has contracts disabled for both the target and the cluster

We see that removing contracts from the code gives use the benchmark time of 0.6 seconds on average.

The all_enabled example and the only_cluster examples effectively evaluate the contracts. This gives us the time of the program with contracts at around 6.25 seconds (the average).

The only_target example effectively disables the contract checking for the root class (enabled for the target but disables for the actual code). Together with the all_disabled example this gives us the time of around 2.7 (the average) seconds to run the program with contracts disabled.

So, compiling with contracts increases the time from 0.6 to 2.7 seconds => 4.5 times increase.

Enabling the contracts increases the time from 2.7 to 6.25 seconds => further 2.31 times increase.

Code comparison

The finalized C code is also present in this repository (subdirectories of results: no_keep, all_enabled etc.). This is to simplify the browsing experience for anyone interested ๐Ÿ˜„

all_disabled vs only_target โ€” no difference

The produced C code differs only in the egc_project_version value. All the code is otherwise the same.

Indeed, enabling contractss for the whole target and then disabling them for all clusters (the only cluster in this example), effectively disables contracts for the whole program.

all_enabled vs only_cluster โ€” no difference

Here again the only difference is the egc_project_version.

Enabling contracts for both the whole cluster and once again for all its clusters (the only cluster in this example) should have the same effect.

no_keep vs all_disabled โ€” the code for all assertions

Discarding assertions by omitting -keep during compilation will result in all assertions-related code to be skipped during compilation to C.

The easiest to observe is the {APPLICATION}.work feature. We will look at the diff of the C3/ap124.c file which contains the code for APPLICATION:

diff -u <(sed -n '73,89p' results/no_keep/C3/ap124.c) <(sed -n '100,155p' results/all_disabled/C3/ap124.c)
 void F280_2080 (EIF_REFERENCE Current)
 {
 	GTCX
+	RTEX;
+	EIF_INTEGER_32 ti4_1;
+	EIF_INTEGER_32 ti4_2;
+	RTSN;
+	RTDA;
 	RTLD;
 	
 	RTLI(1);
 	RTLR(0,Current);
 	RTLIU(1);
 	
+	RTEAA("work", 279, Current, 0, 0, 2802);
+	RTSA(Dtype(Current));
+	RTSC;
 	RTGC;
+	RTIV(Current, RTAL);
+	if ((RTAL & CK_REQUIRE) || RTAC) {
+		RTHOOK(1);
+		RTCT("x.abs <= 200", EX_PRE);
+		ti4_1 = *(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_);
+		ti4_2 = eif_abs_int32 (ti4_1);
+		RTTE((EIF_BOOLEAN) (ti4_2 <= ((EIF_INTEGER_32) 200L)), label_1);
+		RTCK;
+		RTJB;
+label_1:
+		RTCF;
+	}
+body:;
+	RTHOOK(2);
 	(*(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_))++;
+	RTHOOK(3);
 	if ((EIF_BOOLEAN) (*(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_) > ((EIF_INTEGER_32) 200L))) {
+		RTHOOK(4);
 		*(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_) = (EIF_INTEGER_32) ((EIF_INTEGER_32) -200L);
 	}
+	if (RTAL & CK_ENSURE) {
+		RTHOOK(5);
+		RTCT("x.abs <= 200", EX_POST);
+		ti4_1 = *(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_);
+		ti4_2 = eif_abs_int32 (ti4_1);
+		if ((EIF_BOOLEAN) (ti4_2 <= ((EIF_INTEGER_32) 200L))) {
+			RTCK;
+		} else {
+			RTCF;
+		}
+	}
+	RTVI(Current, RTAL);
+	RTRS;
+	RTHOOK(6);
 	RTLE;
+	RTEE;
 }

We can see that going from no_keep to all_disabled mostly introduces the contract checking code. The precondition is checked in the beginning in if ((RTAL & CK_REQUIRE) || RTAC) { ... }, the postcondition is checked in the end if (RTAL & CK_ENSURE) { ... }.

Even though we disable the contracts, the contracts code is added. Additionally, many other features and classes are compiled. TODO: explain why

all_disabled vs only_cluster โ€” only options change

These two examples differ only in two files.

The first file is E1/eoption.c.

The only difference is the line 288:

diff -u results/all_disabled/E1/eoption.c results/only_cluster/E1/eoption.c
 struct eif_opt egc_foption_init[] = {
 ...
-{0, 0, 0, {OPT_ALL, 0, NULL}},
+{63, 0, 0, {OPT_ALL, 0, NULL}},
 ...
 }

The array egc_foption_init is discussed later, you can skip directly to the next part.

Semantically Non-differences

This section is here for completeness sake. You may skip reading it.

The second differing file is again C3/ap124.c. While the files differ, we don't see any difference in symantics.

For example, the only difference in the work feature is using a temporary value to save the result of an expression (only_cluster) compared to using the whole expression in full:

diff -u <(sed -n '100,155p' results/all_disabled/C3/ap124.c) <(sed -n '102,158p' results/only_cluster/C3/ap124.c)
-	if ((EIF_BOOLEAN) (*(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_) > ((EIF_INTEGER_32) 200L))) {
+	ti4_1 = *(EIF_INTEGER_32 *)(Current+ _LNGOFF_0_0_0_0_);
+	if ((EIF_BOOLEAN) (ti4_1 > ((EIF_INTEGER_32) 200L))) {

Similar differences occur in the make feature:

diff -u <(sed -n '60,91p' results/all_disabled/C3/ap124.c) <(sed -n '60,93p' results/only_cluster/C3/ap124.c)
-	loc1 = RTLNS(eif_new_type(784, 0x01).id, 784, _OBJSIZ_0_0_0_1_0_0_0_1_);
-	(nstcall = -1, F785_5095(RTCW(loc1)));
+	tr1 = RTLNS(eif_new_type(784, 0x01).id, 784, _OBJSIZ_0_0_0_1_0_0_0_1_);
+	(nstcall = -1, F785_5095(RTCW(tr1)));
+	loc1 = (EIF_REFERENCE) tr1;

...

-	loc2 = RTLNS(eif_new_type(784, 0x01).id, 784, _OBJSIZ_0_0_0_1_0_0_0_1_);
-	(nstcall = -1, F785_5095(RTCW(loc2)));
+	tr1 = RTLNS(eif_new_type(784, 0x01).id, 784, _OBJSIZ_0_0_0_1_0_0_0_1_);
+	(nstcall = -1, F785_5095(RTCW(tr1)));
+	loc2 = (EIF_REFERENCE) tr1;

The last difference is in the begining of the make feature:

diff -u <(sed -n '46,48p' results/all_disabled/C3/ap124.c) <(sed -n '46,48p' results/only_cluster/C3/ap124.c)
-	RTLR(1,Current);
-	RTLR(2,loc2);
-	RTLR(3,tr1);
+	RTLR(1,tr1);
+	RTLR(2,Current);
+	RTLR(3,loc2);

We should consult the definition of the RTLR macro in eif_macros.h from Eiffel Software's Runtime:

/* Macros used for local variable management:
...
 *  RTLR(x,y) register `y' on the stack at position `x'
...
 */
 
#ifdef EIF_VOLATILE
/* We do a cast to avoid a possible C compiler warnings as `y' will be of type `EIF_REFERENCE EIF_VOLATILE'. */
#define RTLR(x,y) l[x] = (EIF_REFERENCE *) &y
#else
#define RTLR(x,y) l[x] = &y
#endif

Where l seems to be a pointer to the stack.

The macro EIF_VOLATILE can be redefined to be either an empty define (expands to an empty string) or to the volatile modifier. This seems to be needed for exception handling because EIF_VOLATILE is redefined to be volatile for functions having a rescue clause (std_byte_code.e):

if rescue_clause /= Void then
    buf.put_new_line_only
    buf.put_string ("#undef EIF_VOLATILE")
    buf.put_new_line_only
    buf.put_string ("#define EIF_VOLATILE volatile")
end

So, the difference is only in the order of these local variables appearing on the stack.

Overall, we see that the program code is semantically the same. The only real difference is in the egc_foption_init array as was shown before.

Read further for the discussion of that difference.

Eiffel Options: eif_opt

TODO: continue here

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.