 So, welcome to my talk. It's about ScytheN, as the title suggests. Thing is, I regularly speak at Python conferences here and there, and tell people about ScytheN, teach them a bit here and there. I had a tutorial on Tuesday here, don't know if anyone attended it, yeah, still a couple people, cool. So, question now to the audience, I can present a couple of different things depending on what you want. Who's interested in a general introduction to ScytheN, understanding what it is, who's never used it and wants to know what it is? That is quite a number of people. So, the actual topic that I proposed for today was more like, I have some Python code and I want to optimize it without losing the ability to run it in Python, so compile it, speed it up. That's a bit of a less general topic who's interested in that, who came from that. That's about the same number of people. Wonderful. Okay, so I'll just jump back and forth between whatever I can tell you. So, yeah, hi, I'm Stefan Biene, quick intro about myself. I'm a software or data engineer if you want. I'm also a trainer, so I'm given ScytheN trainings here and there. If you want me in-house, just talk to me and I'll come over and teach your people there. I'm using Python since 2002. I'm a ScytheN call developer ever since we forked the project from an existing project at the time. Who knows Pyrex, heard of the name before? Yeah, about a dozen people, yeah, wonderful. That's what we forked and Pyrex has basically not been updated much since then. So, we've pretty much taken over the project and everyone's using ScytheN these days. But thanks, Greg, for writing Pyrex because it was a wonderful tool to build ScytheN on. So, I do trainings, I do consulting and actually joined TrustU, spawned at this conference last year. And as a motivating example, I'll tell you what we do. So, if you look for a hotel in Google, you'll see something like this. You'll get a lot of information about the hotel. And if you click around a bit, you'll also see that some of this information would actually be provided by TrustU. So, we know what a hotel is and how good a hotel is. How do we do that? Well, we crawl the web and we have partners and we read hotel reviews by actual people all around the world, collect them at a rate of about three million hotel reviews every week and then we do text analysis on them, we do data processing on them and we do that in lots of different languages including Spanish, Thai, Mandarin, Japanese, lots of languages and build this kind of information from it. So, we summarize all this. This is a meta review, so just a summarization of everything that 45,000 people have been saying about hotel in this case and we can tell you what's special about the hotel, what people like, what people dislike, all of these things. You can actually go to trustu.com, enter a hotel name there and see this information about any hotel you want pretty much all over the world. So, TrustU, NuzPython, NuzData, we all do all of the data processing in Python and we use the kind of the usual tools you would expect. We use NumPy, we use SciPy, Scikit-learn, Pandas, we use Basitool to a certain extent, LXML for the data extraction from the web and many of these tools actually use Scython, are implemented in Scython, some of them completely, others mostly and they're part of the Python data ecosystem. So, why is that an ecosystem? Well, it's an ecosystem because it's integrated. Everything works nicely together, it chains together and a big part of that came from the fact that people develop NumPy as basically a data integration layer that all of these tools could use, could put their data into, could use to share their data across some different libraries, even pass the data on into C libraries, into native code to processes data. So, NumPy integrates the data layer and you can say that Scython is the way to integrate the code layer because what does Scython give you? It allows you to talk to native code, it allows you to use all these tons of native libraries, C libraries, FORTRON libraries out there, connect them to Python, use them from Python, integrate these libraries from your Python runtime. So, when NumPy integrates data, Scython integrates code. So, what is Scython good for? Well, it integrates native code into Python, that's one use case. Many people use it to speed up Python code by compiling it, compiling it into native code and surprisingly, many people actually use it to write C code without having to write C code, okay? Because, you know, writing C code is actually hard. Writing Python code is much more fun and Scython allows you to write Python code that translates to C. So, writing C without writing C. So, we write the C code so you don't have to. Our topic for today is speeding up Python code. So, I'll kind of focus on that. While you use Scython in general, it's a very pragmatic programming language. It's actually a programming language, so it extends Python. But it is Python, you can take arbitrary Python code, drop it into Scython, compile it. It usually runs faster. But you can also use extended syntax or type annotations in there to speed up your code, to tell the compiler how to optimize your code beyond what the Python language allows. So, it's an optimizing compiler. It's actually very production proven. It's used to run, I mean, it showed you a couple of tools that uses. It's used to process, I don't know, like terabytes, maybe petabytes of data out there in all sorts of Python libraries. It is really widely used. And the cool thing about the language is that it's really about getting things done. In the same way that Python is. It keeps your focus on the functionality rather than having to look into all the boilerplate that you would need to talk to a C library or to speed up your code, to optimize it. If you optimize your code by rewriting it in a native language in C or C++, it gets really messy. It's way more difficult than taking your existing Python code, you tested Python code and just making it faster rather than rewriting it. So, the main property of the language is actually that it allows you to freely move between Python and C or C++. It takes Python code, but it allows you to mix in C data types or C++ data types right in your Python code. And that's the way for the compiler to decide that you as a user, you are actually opting out of Python semantics, Python object operations and saying, you know, all I really need here is a calculation of C doubles, right? Native data types, be fast. I don't care about object operations anywhere. I don't want to pass around stuff through Python and so just make it fast, compute it as fast as you can. And the language makes it very easy. So it allows you to write code that's as Pythonic as you want but as low level as you need. Okay, first demo. Does everyone know the Jupyter notebook? Who does not know it? Nice. It's just, you know, Jupyter is just wonderful. It's a wonderful piece of software. It's a very interactive way to do programming, even data analysis. Lots of people use it to play around with data, visualize it in their browser. And one cool thing of Jupyter is that it does not only support Python, it supports lots of languages. Last time I checked which was years ago and there was already a dozen languages more. It's probably way more now. And one of those languages is Python. All I have to do in Jupyter to make it support Python is load ex-Sython. And I should restart my kernel to show you that it actually works properly. And there you go. Okay, and then just a quick overview of what I'm using here. Latest Python release, latest unreleased Sython. Yeah, I'm a core developer. I can take the risk of using an unreleased Sython version in the talk. Some NumPy version, GC7. And important bit to understand is, you know, in Python, when you write a Python code, you write a module, you save it, you say Python port blah, and it just, you know, imports and runs. In Sython, it's an alpha version. Oh, you mean 029? Yeah, it's 029. Well, it's production proven, but we still say it's 0. something so at some point, ages ago, we said that version 1.0 should be the version that runs arbitrary Python code. And we still have one or two bucks in there that where we have to say, well, we have to fix those first before we can say that we can compile any Python code out there. And so it's still at 0. something and we haven't reconsidered the goal for 1.0. I think we should, because no one cares about full Python compatibility, actually, it's, you know, it works, it's perfect, it's wonderful, and it's just, yeah, it's 0. something. Okay, I consider doing like Chrome and Firefox and just say, you know, this is actually not 0.29, it's 29. something. There you go. Okay, anyway. Quick intro. So, here's a bit of Python code. I say from math import sign, so I'm using the sign function from the Python math module, I calculated sign 5 and says, well, that's about minus 1, more or less. So it works. Now, in order to do that with Python, I can just add these, this line here, which instructs Jupyter to compile this cell in Python instead of just pushing it into Python right there, compile it in Python and it builds an extension module from that that it then imports. Okay, so it builds a shared library and that is also something to keep in mind to do with Python. You step away from the simple write code import try to write code compile import try. So there's a build step involved from that point on. It's usually worth it. Okay, so run it. It's actually pretty quick apparently. I think it was pre-compiled before. So Jupyter caches these cells if it knows that they didn't change. You see a little change in here. What I did before up there is I just said sign 5 and for Jupyter, what Jupyter does is it takes the last expression, the last value that fell out of your cell and displays it. For a Python compiled cell that does not work, why? It's an extension module. It's native code. It's external. Jupyter can't look into it. It doesn't know anything that falls out of it. There's actually nothing falling out of it. It gets executed. It gets imported. And it's imported as a module. There's no value that comes out of it at one time. So if it just ran this, it would display nothing. But I have to be explicit then and say print this. Okay. One difference. This is still, you know, it's a bit boring, right? I'm doing the same thing. I'm taking the Python function, calling it. It doesn't make a difference if I call Python function in compiled code or in imperfective code. It's pretty much the same thing. The nice thing about Python now is I'm not limited to using Python things. Python takes my code, takes my Python code, compiles it to C. Okay. So what I can do now is I can start using C things because, you know, my code ends up in C anyway. And doing C stuff is a totally natural thing to do from C. So instead of importing the math sign function, this static import, and this syntax extension here in Python code. So this is no longer Python code, it's Python code now. I say C import libc math. And that gives me the math header file from libc. Okay. And now I can use the C sign function here. Okay. What I'm doing here is I'm assigning it to a Python variable in my module, compiling that, executing it and question now is what happens when I assign a C function to a Python variable? It becomes callable, right? Python callable. Because it's kind of obvious what this should do, right? I mean, I have a function here. So I can see the function signature and I want it to be a Python thing. So the most obvious thing to do is convert it into a Python callable. What Python does is it generates code for me that wraps the C callable in a Python callable object. I can now call it directly. You'll see that in a minute. Okay. So I have a Python callable now and when I call it from Jupyter, it outputs the same thing as before. There's kind of the quickest way to wrap a C function that has a Python-like signature. Sign symbol just gets a double in, a double out, C double in, double C double out. That means from the Python side you can pass in any float object and get a float object to pick out. Okay. What this basically does internally is this spelled out. So this is a long version of this, right? I'm writing a Python function which says it's called C sign takes a C double, another extension that they can use in the Python now. I can type my arguments. I could just write this. Fine. I would do that in Python. In Python, I can be explicit here and say what I actually want there is a C double. Okay. Whatever you put in as an argument should be converted into a C double and Python will generate the conversion code for me. So what I'll get here is a Python callable function which takes an argument, converts it into a C double. I could type error if that's not possible. So if I put it in string, we get a type error, obviously. And then inside of my function I just call the C sign function. That's what you ask for, right? And I can do that from Jupyter again, call my Python function and it runs the sign function internally. Okay. Nice feature of Cython. When I say Cython minus A what it generates, it generates a C code for me but it additionally generates a little HTML snippet for me that shows me how the compiler understood my code. So this is a copy of my code and it has information that I can use to understand what became of my code. It helps me in optimizing my code. And when I click on one of these lines I can actually see what C code is generated for this line. This is the function signature. It's pretty involved because it has to do error checking. It has to do argument conversion. It has to do lots of things here and there. It has to register a function at a callable in the module argument and all that. So this is what comes out of this line and this line is actually, the second line is actually more interesting. I can see that there's an X argument going in. So we are just, you know, the argument names when we generate C code to avoid naming collisions. This is basically variable X that I put in. I call the C sign function on it and you can see that it's really straight. C call to C sign. And then since this is a Python function the end result has to be passed back into Python, to my Python caller and for that it converts it from a C double to a Python float. And this is what this C API call is doing here. Okay? So this is a nice way for me to, you know, for me as a, as a Python user to understand what Python is doing with my code, how it's interpreting it, what becomes of my code. You can see that there are a couple of yellow lines in here and those yellow lines give me additional information that tell me how much object or operations there are in each of these lines. Which is interesting when I want to start optimizing my code, when I want to, you know, drop it into C and make it faster there because every yellow line tells me there's some object operation going on, there's some exception handling going on, some error checking and that's anything that uses interaction with the C Python runtime. And if my goal is to convert my code into fast C, not using objects, but using native data types than any yellow line is probably worth looking at. And the darker the yellow, the more object operations, the object interaction there is going on. Which is kind of obvious in this case, I mean there's a signature, so like type conversions, error checking, exception handling, stuff like that, lots of interaction going on there and this line is really just converting the end result of a C call back into a Python object, so there's less operations going on here. Okay. Now what makes, I mean this is just a different way basically of spelling the initial example that I had, right? Wrapping a C function, that's it. What makes it more interesting then is in Cython, once I have these, I have a wrapper function here, I can move more code below the Python level, I can take functionality from Python, right, and make my wrapper thicker. I can put functionality into the wrapper between the C library that I'm talking to and the Python API that I'm providing to Python users. So I can make my wrapper more intelligent, smarter, better looking, you know, more Pythonic for a Python user, I put in more functionality at the lower level and hiding all the little dirty quirks in the C API that this Python user shouldn't have to bother with. Okay. Simple example here. Instead of calculating just, you know, just calling the sign function, I call sign of X squared, right? I could do that in Python. I could use my wrapped sign function and do, you know, X squared passing into the sign function and then would call, would calculate the sign of that. But doing that in C is just much faster, right? I pushed down the X, it calculates X squared sign of X squared and then passes back that result much faster than doing half of that in Python. I can do that here. And it runs. Okay. Just two little things. Since we're in C now, I can use manual memory handling in C. This is how that would look like. I have Melloc and Free so I can allocate some memory. I mean, in Python, memory handling is completely automatic, right? You have objects, you have references to them and when the last reference to an object goes away, then the object just dies and gets collected. That is not the case in C. Like, totally not. C is completely manual. And so in C, I would say, you know, allocate some memory here. If that fails, I can now point it back and I just say, raise memory error. You wouldn't really do that in C, right? What would you do in C? You would return some error code, right? Say you return minus one or return something that tells the caller something went wrong and you have to handle it. In Python, well, Python is Python, right? It integrates with Python so you can just say, raise memory error, raise an exception from your Python code. Totally normal. And this is totally what you would do. Well, if the allocation worked, then I can use my memory for something and we have a couple of nice features in the language that make array operations, pointer operations a bit more handy and pythonic than they would be in C. No, since, so question was, are those bound checked? No, because C is just a memory pointer. This is one place in memory and here I'm actually explicitly telling Python to assign something to the first two entries, which is still much nicer than in C, but pointers are just pointers. They're not arrays. Arrays are actually bound checked. Okay, so I can do this and do a couple of nice things. You can also say just print. If anything goes wrong here, then I definitely want to clean up my allocated memory. So I want to free the memory and I do that with try finally, as I would in Python. So if print, for example, raises an exception, can print, send it out anymore, for example, close already, anything can go wrong here. I'm doing a Python thing. So this can raise an exception and if that happens, I say finally, still make sure you free my memory. Your God is what happens. Nice feature. Okay, quick example for calling external code. You've seen Lipsy sign function that I called. Here's an example for calling external library that is not just there, like Lipsy, but really an external library. Who knows what Lua is? Who's used to before? Yeah, okay, cool. So Lua is really a small language. It's a language that people use for embedding, commonly in C++ projects, but you can obviously also embed them in C++. Why not? So here's a little embedding for Lua in Python. First thing I have to do is I have to tell what the C API that I'm using here, the Lua C API looks like. Previously, you may remember that I said C import LipsyMath. What that does is it looks up an external declaration file and finds the LipsyMath declarations in there. Here, I'm doing that inline in a module, and this is a syntax for it. I'm basically just saying, you know, this is an external declaration. It describes external code which comes from the Lua.h file. So that's the header file that describes the Lua API. And it has a couple of things in there. There's a function for creating a Lua runtime for cleaning things up, for loading code into the Lua runtime, and it's on fourth, a couple of functions. And note that I did not copy the complete vast Lua C API in there. I just copied the functions that I need. Because that is all I need in my code. Remember that Python translates my code to C, right? And then there's a C compiler afterwards that compiles that to a shared library. And the C compiler obviously sees the whole thing, right? But Scythe doesn't need to know about the whole thing. It just needs to know what I'm using. In order to generate proper C code that the C compiler can compile and understand. Okay? It's also very nice. It's really just a dozen functions here, so that allow me to execute Lua code from Python. So what's my interface? I'm defining a Python function that takes a code, Lua code as a string. And I'm doing a couple of things. Blah, blah, here. It might be a Unicode string. In Python 3, it's quite likely going to be a Unicode string. In that case, I have to convert it because C doesn't know Unicode. It only knows bytes in strings. And that's how the Lua API works also. So I convert it into A to be able to execute it there. Then I create a new Lua runtime. If that fails, I just say raise memory error because that's what the documentation tells me. If the creation of the Lua runtime fails, documentation says it. Well, it's probably a memory problem, right? It couldn't be allocated. So raise memory error is the right thing. And then whatever happens, I use a try finally to make sure I close down my Lua runtime afterwards and then I run it up. This is what I'm doing down here in my finally. So I'm cleaning up the Lua stack and closing the Lua runtime to clean everything up. To release the memory. And then I have a function to pass in the Lua code into Lua. If that fails, I raise the syntax error. Well, probably the wrong syntax. Then I can execute my code using the C API function and I get some result back. I'm going to be lazy here. I'm just expecting some number back. I could look at what the result is and properly convert it to some corresponding Python type, but here I'm only expecting numbers. One number result and that's it. There's a function to converting it to a C number. And then when I return that from a Python function, Scythe will see okay, there's a C number and you want to return it from Python functions or return it as a Python object. So I compile this. Here's a couple of lines of Lua code. Because of Fibonacci, the usual stupid benchmark. And when I runtime it on that, it's going to tell me that Fibonacci of 24 could be calculated in 2 milliseconds a bit more. Okay? This is how you use C libraries from Python. Okay. So of 10 minutes. So I'll get back here. Second example. Optimizing Python code. I chose DiffLip. Who knows DiffLip? From the standard library. Not so many people. You should look through the standard library documentation. There's lots of nice goodies in there. So what DiffLip does is you can pass two sequences in there and it's going to compare them and tell you where the difference is. It's just like the Unix diff but at an API level. You can use it directly from Python. It's been there for a long time. Most things in the standard library have been there for a long time. So I'm going to optimize DiffLip a bit. Okay. As a benchmark, I'm using a tool called FuzzyWuzzy which comes with a little benchmark. That's why I'm using it. FuzzyWuzzy basically just does fuzzy comparison between text. So it passes into text and I try to match the parts. And it's not really relevant. I'm just using it as a benchmark here. I'm using Cprofile for profiling to see how fast my program is running and where I get. Okay. Let's look at DiffLip. Let's run it first. I'll just clean up my stuff and then run a benchmark. Let's take a couple seconds. It's running multiple examples. The time it takes to run the whole benchmark for FuzzyWuzzy. You'll see in a minute that it's dominated by the time it takes DiffLip to do the comparisons. Takes a bit of time. One thing you shouldn't forget when running benchmarks is some switch of energy handling of your laptop. Okay. In this case because it's been doing computations before and so I'm probably not slowed down much. It's about what I expected. So 46 seconds for the whole runtime. Okay. Now, next thing I'm going to do is I'm running Cprofile on the benchmark to see where time goes. So I'm going to run it again. It's going to take a bit longer but who does not know Cprofile? Okay, couple of people. So Cprofile, what Cprofile does is basically it traces your code. On each function call it's going to tell you functions we called, functions been exited and then afterwards it's going to present you know, it's going to dump the profile somewhere and it's going to tell you what were the functions in there that took most of the time and where was most of the time spent and was less relevant. So it's a very quick way of assessing the runtime profile of your code and like finger point and point that you want to optimize first. Okay, again it takes a while. Obviously it takes longer now because as I said it instruments your code. So it's really doing stuff where your code is running. There are profiles out there that slow down your code much less and it's really worth looking into them. One is the Perf tool in Linux. So if you have Linux look at the Perf, definitely. But it operates, most of these tools operate at a much lower level so they give you C-level information rather than Python-level information and Cprofile just really gives you Python-level information. So I've executed lots of function calls. How many are there? 153 million function calls along the way. It took 78 seconds to run now and the most costly function here is FindLongestMatch in Deflip and it took 25 seconds of our runtime all by itself plus it took an accumulated 34 seconds if you add the its own function calls internal function calls to it. So the whole time spent in that function was 34 seconds and its own code that it uses and 25 seconds only directly in the function itself. So that's a lot of processing time. So I'll look into that function and I have it somewhere here. Can you read this large enough? Larger? Like this? Better? So this is the FindLongestMatch function. It's actually a method which makes it a bit more difficult to optimize but I'll just do a couple of quick speedups here. And it basically starts here. You can see someone's already tried to optimize this function quite a while ago apparently. This is one of the standard tricks that you would pull in Python. It's a bit of an ugly trick. Normally what you would say is A and B and what they do here is they take the bound method of Bs and B contains A. So they replace the operator by function call and apparently that was faster at the time. I actually tested it and it's still a bit faster in 307 but it's something that you don't need in Python because operators in Python are actually much faster. One thing I would do is remove that but they're actually quicker gains to make here and I'll start with those. When you look at the code it has a nested loop and runs over a couple of data structures. You don't need to understand what those data structures actually do. It's a dick involved somewhere, keeps track of stuff dick and list and it's doing things here and there but there's a nested loop. A nested loop always means that there's a lot of work going into looping and to do and stuff there and loops are very easy to speed up in Python because you can replace them by C loops and do stuff that you can see rather than run through Python's iteration protocol. I'll do that first. How do I do that? This is using a loop over a range so I know that these are actually integers and I can replace them by C integers. Most obvious change first. When I look into the usage of I then I can see that it's used to index into a data structure. It's used as key in the dict and it's also used as index somewhere and that means that a difference between C integers and Python integers is that Python integers are unbounded. They can have arbitrary they have an arbitrary value range that can be as large as your memory allows. C integers are not. C integers are always range bounded so this is 32-bit integer which can take numbers up to 2 to 31 and stuff like that. If it's signed and when you're converting Python code to Python now to faster C code you have to take care of that. So I'm replacing Python integers, save Python integers which can go out to large and have to make sure that I'm not restricting the value range in a valid way because C integers wrap around the weird stuff when they go out of bounds but there is a C integer type that I can use here and that is it's called PySizeT which is defined to be large enough to fit the size of the memory so it's a 32-bit system 64-bit system and so on and forth and I'll type all variables in here using that type I'll actually use a different type it's called PySizeT but that's just a different name for it that's used in the Python world I'll use that for my integer variables and I'll type them in in Python so I have an i I have a j as an index I have what else do I have a low a high so there are all index variables here and there is b low and b high when I look through my code those are all the index variables that are being used here and that's also best size just an index best i and best j all of them best i and best j yep, I'm through with my 40 minutes I'll just go a bit into the question time and then run this there so I'll first compile it I'm using Cythonize here so I've compiled my deflip what it does is it just generated a c file for me now it's called in the c compiler so Cythonize is a nice tool for doing like all of them I just say Cythonize my code Cythonize minus i is we built it in place compiles it generates the shared library for me and we can just import it and now when I run my benchmark again it'll take a while it's going to be a lot faster already and while it's running I can just tell you there are a couple of more things that we can do one I already mentioned is I can replace this Python hack here by just the expected operator so undo a Python optimization that is you know that you can do but that is not beautiful and a couple of other things are I'm using dicts here for example dicts and lists here I'm calling the get method of a dict which in Python is looked up every time in Cython that's actually very fast if Cython knows that B2J is a dict and so if I type B2J as dict which is the next thing I can do this is probably an argument so B2J is a dict Python dicts then that's also going to be sped up and you can see we're down from 46 seconds to 35 seconds just by typing the integers and one thing that did is it allowed Cython to replace this integer range loop by a C for loop because all the index variables type so it knows that there's nothing that can wrong I'm actually asking for C for loop here and I'll try the dict optimization next and then I'm already through with my talk and there should be time for one or two questions afterwards so it's compiling and the benchmark again and this is the usual way to go about optimizing code in Cython to take Python code you start typing variables replace them by C data types here and there you use static typing for instructing Cython to opt out of general this is some kind of object semantics into this is a specific data type please optimize it optimize the code for it that is usually faster and definitely more adapted to what you're doing in the code that gives another 5 seconds runtime so we're down from 46 seconds to 30 seconds just by typing a couple of variables here that's it time for a few questions just a couple of questions the first one is like we have seen that you can call random I mean custom C libraries from Python just by defining the interface for them is it possible to do the opposite I mean call Python code from C with using Cython yes so you can generate so-called C def functions in Cython which are just C functions with the expected C signature and you can drop any and you can call the C function from C and then you can execute any Python code in there oh cool and the second question is like whether Cython benefits in some way from using typing hints like in new Python we have the function type hints and so on I've used the syntax for this it does not really benefit all that much from normal typing because there's not so much to gain from them for optimization they are for code checking they're not for optimizing code one last question so we use Cython a lot these are really great tool and one of the things we miss in the Cython ecosystem is a linter some kind of like Flakey or Pepe or whatever we use some subset of what Flakey does like basically for in syntax errors but we wonder if there is any project or idea of maybe exposing the abstract syntax tree or something so we can hook into existing linten tool okay so tool support you can do two things one is as long as you're really just optimizing Python code you can use Python syntax for it and that keeps your code, your optimized code your compilable code in Python syntax and you can use all your Python tools you can start calling to see using native code you lose that ability because it can't be mapped to Python that code cannot run in Python anymore so you use Cython syntax for that in which case there are a couple of well there's at least PyCharm definitely which supports Cython you can use that there probably also other ideas kind of having some basic support but I'm not aware of many many tools for code analysis and this kind of stuff on Cython code specifically is Cython syntax alright, don't forget to rate the talk and let's give Stefan one round of applause