So I'm Dorian, I did that project as part of my studies in HPC in the city of San
Chagre de Compostela and nowadays I'm a programmer for XWiki on totally unrelated
project. So the topic of today is implementing OpenMP for Python. So it's a new library called
OpenMPY, I'm going to talk about another library as well, that does the same thing.
So in Python we have a built-in writing library that's called a setting. That introduces
a thread class and functions together about other things about threads and their other threads,
the current threads and all that threads a lot are pretty running. It introduces, it introduces
the concept of local memory for threads. We have features of synchronizations, but we had
a long time. The problem of having a single thread being able to run at a given moment
I'm sure many of you are aware of the gale. I'm going to come back to that. OK, yes sure. I'll speak
up. So there are some use case for using threading. Despite that limitation, which is
the parallelizing input output, maybe having a GUI where you can have an event-based system.
And also you can still run parallelized code using threading by releasing the gale in functions
implemented in other languages. So we already have some implementations of OpenMPY
Python that are PIOMP and Python, maybe more exists, but those are the ones I came across.
So PIOMP is an extension of a number and it compiles the Python code on the fly to
L to an LL then intermediate representation. And then to machine code. But again, this
comes with limitations. Because we can't run any code written in Python, it's not transparent.
We need to use supported libraries by embed. You need to implement libraries that are implemented
in other languages for them to be compatible. Python is another solution that has built in support
for OpenMPY. However, it's highly limited and it's support of Python itself. Because we
call create classes, we call create classes in Python. So we're limited to, I think, my understanding
of the project was prototyping for mathematical usage. We have ways of parallelizing code in Python,
name for instance, multiprocessing, running different processes, different instances of the Python
operator. And transferring data is an interprocess communication. There is the library
component of futures, which wraps threading and multiprocessing as independent implementations
of the same concept. And I can mention I think you, which is an even based approach similar
to what you can have in JavaScript to have a multi threading, not multi threading, to have
a parallelization of tasks and input output. So recently, some growth in engineer at
Metap, a year ago, I think, opened a map 733. And we're going to fork of Python to remove
the global interpreter lock and make threading in Python actually useful for improving the
performance of programs. So we started with a fork in 3.9 and rebased it in 3.12.
And merged it in 3.30. So the paths have an on 3, it finds that removal of the guild. It was
implemented in 3.30, as I said, which was released recently in October, 2020. So you need
to compile Python with a specific option, which is disabled guild. And at runtime, you need
to also specify that you are running without the guild, because the removal of the guild
has impacts on existing programs. If you're going to run a library that wasn't a program
that wasn't thought with the guild in mind, you're going to run into concurrences issue,
that you didn't have without the guild. Because there is more concurrency than before,
the guild is a global interpreter lock. So when removing it, some instructions, you could
consider that an atomic despite the fact that you are using threads and existing libraries
already use threads and Python, and those will break while when removing the guild. So there
is a lot of work to do to make Python support that removal of the guild.
So I'm introducing a library that's called OMP. You can find it on IP on the Python package index,
not under the name of OMP unfortunately. This one was removed because of AI,
has inading and researchers thinking it's a great idea to claim the name.
So that library implements, sorry, the name for the library on the Python package index
is OPEN and PY. If you're looking for it, I'll show the links later. So the library implements
the OpenMP API. I'm going to go over the different direct chips supported. It's a usage,
that's similar to OpenMP and C. But taking into account the specific cities of Python.
And one of the requirements that we had in that project was to use only Python standard libraries.
So the library doesn't have any dependency.
For the usage of the library, once imported, you can call the OpenMP class.
With the directive as a string parameter. For simple directives and for constructs,
you can use that same class as a context manager. And we also implement the primitives
simply under the OMP name space. So I'm going to show some example of code.
So that you can have an idea of what it looks like.
Okay. So here I'm importing the library on P. I need to decorate my main function
with OMP.enable. And what that does is it's going to read the source code of that function
and do a sort of pre-processing for it to do the correct translation of the OMP directives
of the OpenMP directives so that it's doing the threading and managing the virus.
So in that example, it's a pretty simple one. I'm just summing the numbers from one to
number and at this defined here. That's big so that we can see the performance.
So I'm using a reduction and the schedule parameter is
sorry. The schedule parameter is
something that's important to tweak for performance especially in Python. And that's a big difference
that will seems with the usage of OpenMP you can have in C because you need to take into account
the base performance of Python. So this is a native Python implementation of OpenMP.
So we're still going at the pace of Python. We're not going faster, we're just
paralyzing what's happening. So that's the usage of the library. When removing the
decorator, OpenMP.inable, the code is run seamlessly as if we didn't have the OpenMP directives.
Those don't do anything when the decorator is not present.
So on the performance here I tried running that code with the different
tranche sizes. And what we can see is that we do have a speed up. So that's great news.
But it's not great. So here you have the time on a log scale and here are the number of threats.
I was able to run my code on a super computer node at a finished array
three in in Galicia and says gap. So thanks to them. I don't have access to that computer anymore.
I can't run more advanced examples, but I sure you have I'm sure you have many ideas of
usage for that library that fits your purpose. So about the chunk size, what's interesting
is that there is a sweet spot where we gain some time. So we're using a dynamic scheduling and
Python's syntax is a tricky to manage because this is a for loop and so this is an iterator
and the way implemented the library is that it works for any iterator here, not only range.
So so far the dynamic scheduling is going to unpack that iterator and distribute it to
the different threats as we're going. So when running with a short body like this one that's
only doing one operation, it's going to be we're going to lose time simply unpacking the range itself.
And unpacking the range takes most of the time compared to simply updating the accumulator.
So I have another example where I defined a function a very simple one that checks if a number is
prime. So it's meant as a heavier computation than just adding a number to an accumulator
since I have to go prime first on bigger and bigger numbers. So here we can see the difference again
in runtime depending on the number of threat. And we achieve a much better speed up
which I think showcases the time we lose unpacking the range. So there are some optimizations
that can be done to that unpacking by specifically having an implementation for the
further ranges and still fold back to that unpacking distribution for other iterators.
Yeah, it uses multi threading under the hood, nothing fancy here. But we do have to manage
variable scopes. So the nice thing about sorry. So the nice thing about that library
compared to other techniques of multi threading is you don't have to care much about the
variable scope. It's exactly like an MP. So that accumulator I can access it here. I can
access it outside of the thread when I print my result. When I outside of the
threat. So that's that. On the supported directives from OpenMP, not all of them. We have
parallel with a private clause. We have a four with no a private reduction in schedule. We have
a barrier directed. We have a critical, single and that's it. So that's the support that's currently
implemented. I'm interested if you have ideas of usage or how it could be useful to you and what
could be missing in that library. And yeah, that's it.
Question is the door in. Yeah, all right. There are a number of clauses you
call OpenMP. Yes. So the question is, is there a documentation for what the clauses do?
So we're just following the OpenMP specification here. Is this so new?
No, sorry. It's not ready to go. It's blinking.
Let me find it. Yeah. So you can read the OpenMP API and you have the explanation of what
the clauses do. I'm just following this. I did do some modification for the four
directive for the scheduling. I changed the default. I switched it to dynamic. Because of how
the fact that I don't have a specific range implementation affects the performance. So I decided
that dynamic was a better default. All right. So we continue to see C8 here. Yes, other than
that you need to call OpenMP. Yes, another question. Go ahead.
So it depends on the task. Yes, sorry. Good point. So about the runtime that I showed,
it seems that it doesn't scale above six cores. We have that here. Yes.
Maybe I'll put that document online. So you can access it on the presentation page.
Yes, it depends on the kind of task. On that task, specifically. Yes, we do reach a minimum,
pretty quickly, where adding more cores doesn't bring any benefit. And that's because of the
unpacking of the range. So what I get from that is we really need to have an implementation
specifically for the range and not unpacking the range as any iterator and distributing the elements.
So the question is, with the different workload, could I scale to 60 cores?
The answer is yes. Here is the different workload. And you can see here,
let's keep gaining time. And I do have the speed up chart here. So that talks more maybe.
So for 64 cores, we're at about a speed up of 50. So that's a heavier task. It's a different one.
We do achieve a speed up. We are able to compute different things at the same time.
More questions. Go ahead.
Okay. So what I'm going to do is I have to do something about what happens,
so the question is, given that we read the iterator elements and distribute them across
threads, what happens if the iterator is infinite?
Pretty much the same thing as if you weren't using OpenMP. So you have an infinite iterator.
Your fall loop is going to run infinitely. That's it.
Other questions?
How does it compare with the deficit? How does it compare with the deficit package?
So the question is, how does it compare to multiprocessing? The library multiprocessing?
That's an interesting question. The goal of that library is purely syntax sugar.
It's not about making multi-threading performance better in Python.
So I didn't do a comparison of the performance with multi-threading. I think it's more of a
something that's interesting to work at Sandroza for the meaning of removing the meal itself.
Sorry, I can't answer it.
The more question, the more thing clearly on this lovely report,
but this report, can you put it in the slide? I didn't understand the question, can you do it?
Well, you're showing your report.
Yes, I'm going to put it in the line. Sorry, I didn't know.
Yes, I would be sorry if you Dialge The Passion is about the money.
Great question.
Okay?