Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve checking for nil Buffers #2960

Merged
merged 4 commits into from Jun 24, 2017

Conversation

patrickdupuis
Copy link
Contributor

This PR is a redo of my previous work on checking for freed Buffers. The checks now happen inside the Msg methods. OSCFuncs have also been moved inside these Msg methods. The Msg methods are now consistently called in order to construct the Server messages.

Thank you for testing and reviewing!

Copy link
Member

@telephon telephon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I've done a speed comparison:

MyTest {

	setnMsg { arg ... args;
		^["/b_setn", 7] ++ args
	}

	setn { arg ... args;
		this.whatever(this.setnMsg(*args));
	}
	setn2 { arg ... args;
		this.whatever(["/b_setn", 7] ++ args);
	}

	whatever { arg args;
		^args
	}


}

bench { 1000.do { a.setn([1, 2, 3]) } };

and

bench { 1000.do { a.setn2([1, 2, 3]) } };

here, the second (direct) call is about 1.5 times faster. Because this is a core class, this is important.

}

set { arg index, float ... morePairs;
if(bufnum.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw };
server.listSendMsg([\b_set, bufnum, index, float] ++ morePairs)
server.listSendMsg(this.setMsg(index, float, *morePairs))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will slow down performance of this method, especially if you send many morePairs. Better some duplicate code in this case (I know, it's not beautiful, but it is a bottleneck).

^[\b_set, bufnum, index, float] ++ morePairs
}

setn { arg ... args;
if(bufnum.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw };
server.sendMsg(*this.setnMsg(*args));
server.listSendMsg(this.setnMsg(*args));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here.

}

get { arg index, action;
if(bufnum.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw };
server.listSendMsg(this.getMsg(index, action));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here it's ok.

^[\b_get, bufnum, index]
}

getn { arg index, count, action;
if(bufnum.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw };
server.listSendMsg(this.getnMsg(index, count, action));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also ok here.

^[\b_getn, bufnum, index, count]
}

fill { arg startAt, numFrames, value ... more;
if(bufnum.isNil) { Error("Cannot call % on a % that has been freed".format(thisMethod.name, this.class.name)).throw };
server.listSendMsg(["/b_fill", bufnum, startAt, numFrames.asInt, value] ++ more)
server.listSendMsg(this.fillMsg(startAt, numFrames, value, *more))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not so good here.

+ (asWavetable.binaryValue * 2)
+ (clearFirst.binaryValue * 4)]
++ genArgs)
server.listSendMsg(this.genMsg(genCommand, genArgs, normalize, asWavetable, clearFirst))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure here.

+ (asWavetable.binaryValue * 2)
+ (clearFirst.binaryValue * 4)]
++ amps)
server.listSendMsg(this.sine1Msg(amps, normalize, asWavetable, clearFirst))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not good to forward here.

+ (asWavetable.binaryValue * 2)
+ (clearFirst.binaryValue * 4)]
++ [freqs, amps].lace(freqs.size * 2))
server.listSendMsg(this.sine2Msg(freqs, amps, normalize, asWavetable, clearFirst))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not good to forward here.

+ (asWavetable.binaryValue * 2)
+ (clearFirst.binaryValue * 4)]
++ [freqs, amps, phases].lace(freqs.size * 3))
server.listSendMsg(this.sine3Msg(freqs, amps, phases, normalize, asWavetable, clearFirst))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not good to forward here.

+ (clearFirst.binaryValue * 4)]
++ amplitudes)
cheby { arg amps, normalize=true, asWavetable=true, clearFirst=true;
server.listSendMsg(this.chebyMsg(amps, normalize, asWavetable, clearFirst))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not good to forward here.

@telephon
Copy link
Member

I just noticed that so far, the existing code is also not consistently caring for efficiency. So if you feel like improving it, that is good, but if you prefer to just leave this PR as it is, I can do this some other time.

@jamshark70
Copy link
Contributor

jamshark70 commented Jun 18, 2017

I kind of hate to say this after all the work rejiggering the methods, but... I'm nervous about coupling the OSCFuncs with the message creation. When I started writing this, I was only a little nervous, but after finishing my remarks, actually I'm considerably nervous about it.

  • Pro (OSCFunc in getMsg): get, getn, query etc. only make sense in a RT server, and if you're dealing with a RT server, it's nonsense to create a message and then never send it. Flipside: the normal use case for calling a ...Msg method is to collect commands into a Score for NRT processing... but get is nonsense in NRT. So, sure, in theory there's the potential of creating orphan OSCFuncs, but in normal use, that won't happen.

  • Con (OSCFunc in get instead): With the design currently in master, a user could respond to /b_get's reply with a custom OSCFunc, by using getMsg (<-- edit, typo, sorry), making her own responder, and sending the message manually. With the change, it becomes mandatory to install the standard responder, even if the user isn't interested in it. That is, the proposed change here assumes that the user will necessarily send the message and will necessarily use the standard responder -- but we don't know that. In general, programming languages should give tools to the user without ruling out what might appear to be creative abuse.

TBH I find the Con position more convincing. I can understand the reason for moving the OSCFuncs, but I disagree with it.

@patrickdupuis
Copy link
Contributor Author

Thanks @telephon and @jamshark70

OSCFunc placement

I was only 50/50 on this one anyway. James seems to have a good point. I can move them back. I wonder: is there a .listSendMsgWithReply method we can use instead of having to create the OSCFunc explicitly?

Performance and code duplication

I made these changes trying to avoid code duplication (both the bufnum.isNil check and Msg creation). It is not acceptable to decrease performance, however. I wonder if I need to go about this in the opposite way. All methods check for nil Buffers, and no methods call their corresponding ...Msg methods. That way the code will be consistent and have better performance, but at the cost of duplication.

@telephon
Copy link
Member

telephon commented Jun 18, 2017

I didn't notice the OSCFuncs in the message creation code. I think it's not good to put it there, because it is supposed to just create the messages, nothing else.

maybe the best solution is: make a test method like throwIfNotReady and call that method everywhere. Then the code is still well readable.

@patrickdupuis
Copy link
Contributor Author

This moves the OSCFuncs back to where they were initially.

Next up is the server messages.

@patrickdupuis
Copy link
Contributor Author

These are the changed I now propose :

  • Reverted the changes made to the OSCFuncs
  • Methods no longer call their respective ...Msg methods, which should improve performance a little over what was being done before (write and writeMsg for example)
  • All methods check the Buffer's validity without then checking it again when calling ...Msg

Thank you for reviewing and testing 👍

Copy link
Member

@telephon telephon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so now all that seems missing is the test for the "direct" methods. e.g. fill is missing it.

see e.g.


	fill { arg startAt, numFrames, value ... more;
		server.listSendMsg([\b_fill, bufnum, startAt, numFrames.asInt, value] ++ more)
	}

	fillMsg { arg startAt, numFrames, value ... more;
		if(bufnum.isNil) { Error("Cannot construct a % for a % that has been freed".format(thisMethod.name, this.class.name)).throw };
		^[\b_fill, bufnum, startAt, numFrames.asInt, value] ++ more
}

@patrickdupuis
Copy link
Contributor Author

Make sure you are looking at the latest changes. fill should have this check, as well as all the other methods.

@telephon
Copy link
Member

ah good - sorry for the noise. Somehow the webpage showed me the wrong version.

@telephon
Copy link
Member

@patrickdupuis is this ready to go?

@patrickdupuis
Copy link
Contributor Author

Maybe @jamshark70 or @snappizz can give a look? The PR might need labelling and whatnot for the 3.9 change log.

@patrickdupuis patrickdupuis changed the title check for nil Buffers [take 2] Improve checking for nil Buffers Jun 19, 2017
@mossheim mossheim added the comp: class library SC class library label Jun 22, 2017
@telephon
Copy link
Member

looks good to me

@jamshark70
Copy link
Contributor

Ok for me too.

@telephon telephon merged commit 223b6fc into supercollider:master Jun 24, 2017
@patrickdupuis patrickdupuis deleted the topic/buffer-bus-check branch June 24, 2017 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp: class library SC class library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants