Thread overview
are scope guards (scope(exit, success, failure)) zero-cost abstractions?
Feb 08, 2018
Timothee Cour
Feb 08, 2018
Mike Parker
Feb 08, 2018
Timothee Cour
Feb 08, 2018
Daniel Kozak
Feb 08, 2018
Daniel Kozak
Feb 08, 2018
Seb
Feb 08, 2018
Seb
February 08, 2018
I'm curious whether scope guards add any cost over the naive way, eg:

```
void fun(){
  ...
  scope(success) {bar;}
  ...
}
```

vs:

```
void fun(){
  ...
  if(foo1){
    bar;  // add this before each return
    return;
  }
  ...
  bar;
  return;
}
```

For scope(success) and scope(failure), the naive way would anyway
involve try/catch statements but what about scope(exit)? Does the
zero-cost exception model (zero cost being for non-thrown exceptions)
guarantee us that scope(success) has 0 overhead over naive way?
February 08, 2018
On Thursday, 8 February 2018 at 10:09:12 UTC, Timothee Cour wrote:
> I'm curious whether scope guards add any cost over the naive way, eg:
>
> ```
> void fun(){
>   ...
>   scope(success) {bar;}
>   ...
> }
> ```
>
> vs:
>
> ```
> void fun(){
>   ...
>   if(foo1){
>     bar;  // add this before each return
>     return;
>   }
>   ...
>   bar;
>   return;
> }
> ```
>
> For scope(success) and scope(failure), the naive way would anyway
> involve try/catch statements but what about scope(exit)? Does the
> zero-cost exception model (zero cost being for non-thrown exceptions)
> guarantee us that scope(success) has 0 overhead over naive way?

Scope guards are lowered to the equivalent try/catch/finally blocks anyway.
February 08, 2018
I know that, my question is whether it adds any runtime overhead over naive way (which is to call the "bar" finalizer before each return statement)  in the case where no exception is thrown


On Thu, Feb 8, 2018 at 2:44 AM, Mike Parker via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
> On Thursday, 8 February 2018 at 10:09:12 UTC, Timothee Cour wrote:
>>
>> I'm curious whether scope guards add any cost over the naive way, eg:
>>
>> ```
>> void fun(){
>>   ...
>>   scope(success) {bar;}
>>   ...
>> }
>> ```
>>
>> vs:
>>
>> ```
>> void fun(){
>>   ...
>>   if(foo1){
>>     bar;  // add this before each return
>>     return;
>>   }
>>   ...
>>   bar;
>>   return;
>> }
>> ```
>>
>> For scope(success) and scope(failure), the naive way would anyway
>> involve try/catch statements but what about scope(exit)? Does the
>> zero-cost exception model (zero cost being for non-thrown exceptions)
>> guarantee us that scope(success) has 0 overhead over naive way?
>
>
> Scope guards are lowered to the equivalent try/catch/finally blocks anyway.
February 08, 2018
Yes, it add, but is almost zero

On Thu, Feb 8, 2018 at 12:00 PM, Timothee Cour via Digitalmars-d-learn < digitalmars-d-learn@puremagic.com> wrote:

> I know that, my question is whether it adds any runtime overhead over naive way (which is to call the "bar" finalizer before each return statement)  in the case where no exception is thrown
>
>
> On Thu, Feb 8, 2018 at 2:44 AM, Mike Parker via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
> > On Thursday, 8 February 2018 at 10:09:12 UTC, Timothee Cour wrote:
> >>
> >> I'm curious whether scope guards add any cost over the naive way, eg:
> >>
> >> ```
> >> void fun(){
> >>   ...
> >>   scope(success) {bar;}
> >>   ...
> >> }
> >> ```
> >>
> >> vs:
> >>
> >> ```
> >> void fun(){
> >>   ...
> >>   if(foo1){
> >>     bar;  // add this before each return
> >>     return;
> >>   }
> >>   ...
> >>   bar;
> >>   return;
> >> }
> >> ```
> >>
> >> For scope(success) and scope(failure), the naive way would anyway
> >> involve try/catch statements but what about scope(exit)? Does the
> >> zero-cost exception model (zero cost being for non-thrown exceptions)
> >> guarantee us that scope(success) has 0 overhead over naive way?
> >
> >
> > Scope guards are lowered to the equivalent try/catch/finally blocks
> anyway.
>


February 08, 2018
I mean scope(success), for scope(exit) there is no speed penalty

On Thu, Feb 8, 2018 at 12:03 PM, Daniel Kozak <kozzi11@gmail.com> wrote:

> Yes, it add, but is almost zero
>
> On Thu, Feb 8, 2018 at 12:00 PM, Timothee Cour via Digitalmars-d-learn < digitalmars-d-learn@puremagic.com> wrote:
>
>> I know that, my question is whether it adds any runtime overhead over naive way (which is to call the "bar" finalizer before each return statement)  in the case where no exception is thrown
>>
>>
>> On Thu, Feb 8, 2018 at 2:44 AM, Mike Parker via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
>> > On Thursday, 8 February 2018 at 10:09:12 UTC, Timothee Cour wrote:
>> >>
>> >> I'm curious whether scope guards add any cost over the naive way, eg:
>> >>
>> >> ```
>> >> void fun(){
>> >>   ...
>> >>   scope(success) {bar;}
>> >>   ...
>> >> }
>> >> ```
>> >>
>> >> vs:
>> >>
>> >> ```
>> >> void fun(){
>> >>   ...
>> >>   if(foo1){
>> >>     bar;  // add this before each return
>> >>     return;
>> >>   }
>> >>   ...
>> >>   bar;
>> >>   return;
>> >> }
>> >> ```
>> >>
>> >> For scope(success) and scope(failure), the naive way would anyway
>> >> involve try/catch statements but what about scope(exit)? Does the
>> >> zero-cost exception model (zero cost being for non-thrown exceptions)
>> >> guarantee us that scope(success) has 0 overhead over naive way?
>> >
>> >
>> > Scope guards are lowered to the equivalent try/catch/finally blocks
>> anyway.
>>
>
>


February 08, 2018
On Thursday, 8 February 2018 at 10:44:37 UTC, Mike Parker wrote:
> On Thursday, 8 February 2018 at 10:09:12 UTC, Timothee Cour wrote:
>> I'm curious whether scope guards add any cost over the naive way, eg:
>>
>> ```
>> void fun(){
>>   ...
>>   scope(success) {bar;}
>>   ...
>> }
>> ```
>>
>> vs:
>>
>> ```
>> void fun(){
>>   ...
>>   if(foo1){
>>     bar;  // add this before each return
>>     return;
>>   }
>>   ...
>>   bar;
>>   return;
>> }
>> ```
>>
>> For scope(success) and scope(failure), the naive way would anyway
>> involve try/catch statements but what about scope(exit)? Does the
>> zero-cost exception model (zero cost being for non-thrown exceptions)
>> guarantee us that scope(success) has 0 overhead over naive way?
>
> Scope guards are lowered to the equivalent try/catch/finally blocks anyway.


Yes, let's look at it:


```
scope(failure) printf("%s", "error".ptr);
printf("%s", "All good.".ptr);
```

1) Lowered AST

https://run.dlang.io/is/PmRfkb

---
import object;
import core.stdc.stdio;
void main()
{
	try
	{
		printf("%s", "All good.");
	}
	catch(Throwable __o2)
	{
		printf("%s", "error");
		throw __o2;
	}
	return 0;
}
---

2) Assembly

(reduced to the interesting bits - see https://run.dlang.io/is/Cafgja for the full ASM)

---

		call	  printf@PLT32
		jmp short	L69
		lea	RSP,0FFFFFFF0h[RBP]
		mov	-8[RBP],EDX
		mov	RDI,RAX
		call	  __dmd_begin_catch@PLT32
		mov	-010h[RBP],RAX
		mov	EAX,-8[RBP]
		cmp	EAX,1
		je	L3F
		jmp short	L62
L3F:		lea	RSI,FLAT:.rodata[00h][RIP]
		lea	RDI,FLAT:.rodata[00h][RIP]
		xor	EAX,EAX
		call	  printf@PLT32
		mov	RDI,-010h[RBP]
		call	  _d_throwdwarf@PLT32
		mov	RSP,RBP
		pop	RBP
		ret
---
February 08, 2018
On Thursday, 8 February 2018 at 11:23:43 UTC, Daniel Kozak wrote:
> I mean scope(success), for scope(exit) there is no speed penalty
>
> On Thu, Feb 8, 2018 at 12:03 PM, Daniel Kozak <kozzi11@gmail.com> wrote:
>
>> Yes, it add, but is almost zero
>>
>> On Thu, Feb 8, 2018 at 12:00 PM, Timothee Cour via Digitalmars-d-learn < digitalmars-d-learn@puremagic.com> wrote:
>>
>>> I know that, my question is whether it adds any runtime overhead over naive way (which is to call the "bar" finalizer before each return statement)  in the case where no exception is thrown
>>>
>>>
>>> On Thu, Feb 8, 2018 at 2:44 AM, Mike Parker via Digitalmars-d-learn <digitalmars-d-learn@puremagic.com> wrote:
>>> > On Thursday, 8 February 2018 at 10:09:12 UTC, Timothee Cour wrote:
>>> >>
>>> >> I'm curious whether scope guards add any cost over the naive way, eg:
>>> >>
>>> >> ```
>>> >> void fun(){
>>> >>   ...
>>> >>   scope(success) {bar;}
>>> >>   ...
>>> >> }
>>> >> ```
>>> >>
>>> >> vs:
>>> >>
>>> >> ```
>>> >> void fun(){
>>> >>   ...
>>> >>   if(foo1){
>>> >>     bar;  // add this before each return
>>> >>     return;
>>> >>   }
>>> >>   ...
>>> >>   bar;
>>> >>   return;
>>> >> }
>>> >> ```
>>> >>
>>> >> For scope(success) and scope(failure), the naive way would anyway
>>> >> involve try/catch statements but what about scope(exit)? Does the
>>> >> zero-cost exception model (zero cost being for non-thrown exceptions)
>>> >> guarantee us that scope(success) has 0 overhead over naive way?
>>> >
>>> >
>>> > Scope guards are lowered to the equivalent try/catch/finally blocks
>>> anyway.

Yes, it's easy to see this when looking at the lowered AST and ASM.

1) AST

https://run.dlang.io/is/KNJbnP

---
import object;
import core.stdc.stdio;
void main()
{
	printf("%s", "All good.");
	printf("%s", "FOO");
	return 0;
}
---

2) ASM

https://run.dlang.io/is/bIVYvi

---
_Dmain:
		push	RBP
		mov	RBP,RSP
		lea	RSI,FLAT:.rodata[00h][RIP]
		lea	RDI,FLAT:.rodata[00h][RIP]
		xor	EAX,EAX
		call	  printf@PLT32
		lea	RSI,FLAT:.rodata[00h][RIP]
		lea	RDI,FLAT:.rodata[00h][RIP]
		xor	EAX,EAX
		call	  printf@PLT32
		xor	EAX,EAX
		pop	RBP
		ret
---