A peek at IPC and RPC / Coup d’oeil à l’IPC et au RPC

In computer science, we like to run programs. We write lines of codes that take in some input, feed it to a set of gears for computation, and spit out a result. This is hardly news. But what is not clear at first when you’re a programmer in training is all the complexity this sentence hides. To better understand this, let’s talk about washing dishes, doing some shopping and going to the laundry mat.

This article is also available on Medium.

I’ve recently had to think quite a lot about this at work. A question we were facing was “security vs rapidity”. Basically: say you have a piece of code that can come from anywhere – then should you just run it and hope for the best? or should you instead implement safeguards so that it doesn’t break your computer, erase all your files, or simply crashes and stands there useless in your all workflow?

Running a program is like using any other tool in your everyday life: you need to take into account who made it to understand how it works, who will use it and why, what environment it will be used in… This lead me to do some research on IPC (Inter-Processus Communication) and RPC (Remote Procedure Call).Disclaimer: for the ones into computer science: RPC is technically IPC too, since it does involve two processes communicating with each other. In this article, however, I will consider IPC to be strictly local (and therefore will distinguish between “on-the-same machine” and “across a network” situations).

Let’s take an analogy!

Suppose you have a somewhat boring day ahead of you: you need to wash your dishes, buy some groceries and do your laundry. These 3 tasks can be thought of as small programs that each “transform” stuff into something else: washing your dishes will require dirty dishes and “produce” clean ones, buying groceries should convert some of your money into vegetables and bread, and doing the laundry is the same as the dishes thing but with clothes.

Now, let’s assume a few more things:

  • we have a dishwasher, so “doing the dishes” can be translated to: loading up the dishwasher, setting up the washing sequence, pressing start and collecting the result at the end
  • we have a washing machine but it’s old and rusty – it basically takes twice the time it should to take care of your clothes and some aren’t completely clean afterwards
  • on the other hand, there is a laundry service next door (literally)
  • buying groceries requires us to be there the whole time, naturally

There is one very easy – but tedious! – way of going about this day: simply take care of each problem one at a time and wait for each process to complete before ticking it on your list. This would mean that, for example, you load up your dishwasher and then patiently sit beside it the whole time it’s working its magic. A few hours later, you put back the shiny plates in your cupboards and head on for the next task. At the store, you go through the shelves and fill up your basket, then go and pay for the whole thing. Finally, you’re to do the laundry. Just like you did with the dishes, you’ll put the clothes in the old washing machine, wait even longer and eventually end up with fresh half-cleaned clothes.

Here is a basic representation of all the chores to accomplish if we decide to complete one after the other:

An overview of the 3 chores on the to-do list with the intermediary steps for each one. The light redish background highlights the steps that don’t actually require us to stand by. The blue arrow represents time flowing: here we do each step one after the other.

This is called “serial execution” because you run a series of tasks. We can make a small diagram to show how things would go down:

Serial execution consists in running each task one after the other: the total duration of the to-do list is the sum of the duration of every item.

We see that, in total, it would take us about 5 hours do take care of our to-dos.

You might note at this point that is quite a silly schedule. In particular, some tasks like “watching the dishwasher do its business” are absolutely useless. Conversely, the grocery store, while it does not necessarily take the biggest chunk of our time, requires us to be there at every step to keep on picking stuff from the shelves.

This is the difference between a “synchronous” and an “asynchronous” task.

Synchronous and asynchronous tasks

Just as in our little example, when you want to run two different programs, sometimes, you might come to the realization that:

  • they don’t actually need one another to work (here: washing dishes and buying groceries aren’t link, you can do them in whichever order you prefer, whenever you want, however you choose)
  • some can be “started off” and then you can mind your own business until they’re finished without standing by and watching it: those are asynchronous tasks
  • but others need you to regularly do something and therefore you can’t leave them alone: those are synchronous tasks

There are, of course, advantages and drawbacks to both.

Synchronous tasks can be time-consuming because they force you to stay focused on the task at hand. In computer science, we talk of “blocking” instructions: certain lines of code that work synchronously might take a while to finish entirely (for example: “buy groceries”) and until this instruction is done you are stuck and can’t go to the next task on your list (the next instruction in your program(s)). The upside is, however, than when you’re (finally) done with the instruction, you are absolutely sure the process has transformed the initial input into the desired output (well, it can error, or be wrong, but the main thing is: the “transformation” is complete, it is not a half-baked Frankenstein in-between thing). This means that if everything went well, you are completely sure of the state your program is in at this stage.

On the other hand, asynchronous tasks take the opposite road: you need to be very careful when you look back at your results (say the dishwasher isn’t done and you take the plates: they won’t be fully cleaned!) but you don’t need to hold their hand until they’re finished. There are numerous examples of asynchronous tasks: queries in a big database, long mathematical computations, independent file processing… All of these are used more often that we think as part of the bigger tech-products we use everyday:  search engines, social networks, online shops, etc. The core idea behind asynchronous programming is that, rather than waiting for the task to complete and keeping an eye on it, you wait for the task itself to tell you when it’s done. This way, you can work on your own thing, forget about the asynchronous task completely and get some notification when the asynchronous task has it has completed. This is what your dishwasher does when it rings at the end of the washing process: it tells you that it’s finished so you incorporate this information to the rest and act upon it.

A final important note on our “washing the dishes” and “buying groceries” tasks: as we mentioned earlier, there is no inherent link between the two. They don’t use the same input and don’t rely on one another. This means that, in an ideal world where we could be everywhere at once, we could do both at the same time without any issue. In programming terms, we say they can be parallelized (they can live on “parallel” life lines).

Compared to the serial execution we mentioned before, parallel execution can be represented like this:

Parallel execution makes sure that independent tasks are run at the same time so we don’t lose time unnecessarily. The total duration of the to-do list is the duration of the slowest task (here, doing the laundry with our old washing machine).

Note: here, I indicate “local” in the title because we will see later the difference between local (IPC) and remote (RPC) execution and how it affects the total duration of our chores.

We notice that by “grouping” together the tasks, we gain a little under 3 hours of free time, as opposed to the previous “full sequential” scheme! This is usually the tradeoff we want to make in computer science when we use parallel execution: we devote a bit more time to writing the initial program, and a bit more resources to the execution, so that we can get a huge reduction of the execution time in return.

Though it is by no mean easy to do, to me, a good programmer should always try and think about what can and cannot be asynchronous or not to make the best out of current tools in the domain. Because we do have a lot of tools! Over the years, engineers have developed amazing pieces of hardware and software to gradually improve our ability to parallelize tasks.

Working on your own: (local) IPC

You might think that having a single computer means you can only run one task at a time. But not anymore! It used to be that way, but today we have several ways of bypassing this using threads or GPUs for example.

Like a conductor performing an opera…

On a computer, the different tasks currently scheduled for treatment are called “processes”. So here we’d have one process that is “washing dishes”, another “buying groceries” and a third one “doing laundry”. Each process is composed of a set of instructions and an execution context; in our case, for example, the “washing dishes” could be divided into:

  • a set of instructions: load up the dishwasher, prepare the washing sequence, press the start button and collect the washed dishes
  • a context: what dishes we are supposed to wash this time, whether we’ve already prepared the washing sequence or not…

As you can see, generally speaking, the set of instructions is fixed (it is the program we have written beforehand and it contains the same data each time) whereas the context is more volatile: it evolves as the process executes and it may contain “dynamic” data, data that depends on the moment we execute the process. We may have lots of plates to wash one day, and cutlery instead the next.

Inside each computer, your operating system, or OS (Windows, Linux, Mac OS X…), is the conductor of a grand orchestra. Among other things, it has to deal with all processes currently scheduled to insure that they each get enough resources to complete the operation properly. The problem is that a computer is inherently sequential; in truth, it can not multi-task. Still our modern architectures are able to simulate multi-tasking extremely well.

The key is to switch between processes very quickly so that each gets a little piece of the cake regularly and keeps on running through the list of instructions it has to complete. If you want to feel like you’re making progress on all of your chores, you could jump around to execute a small instruction for each of your 3 tasks, move on to the next one, circle back to the first one, and so on.

Going back to our basic tasks-diagram, this is how it would change:

As opposed to the serial execution, we don’t wait for a to-do to finish completely before tackling the next one. Instead, we just remember where we stopped last time and have all of the tasks progress at the same time. Still, we see that there is some latency between each step, it takes a bit of time to get from one task to another. So the issue is: how can we switch between tasks quickly enough to have something resembling multi-tasking?

Note: in a computer, “remembering the last stop” is done by storing what is called an instruction pointer as part of the process context. Whenever the OS decides it is turn for one parallel process to lead again (for a little while, that is), it restores the context and in particular goes back to the last executed instruction thanks to this pointer.

There are different ways of doing parallelized execution, the most common ones being threads and GPUs. These are novel solutions to our process-switching problem that make for a very short transition time. Threads are said to be lightweight because reviving them to continue on with the next step is really quick. Thanks to this sort of tech, we can reduce the switching time so much that it feels like we’re multi-tasking:

And we’ve finally gotten parallel execution!

The term IPC (inter-process communication) refers to the fact that today, we can have some interactions between our processes, exchange data…

Delegating to someone else: RPC

So far, we’ve focused on a local solution, i.e. computer tech that doesn’t involve communication between multiple computers over a network. In our analogy, it corresponds to the fact that we’ve used our own dishwasher and washing machine to complete our chores.

Another possibility to do remote execution via remote procedure calls (RPC) to have multiple processes spread across multiple computers that communicate with each other (with non-local inter-process communication).

Taking advantage of better resources

Do you remember how we have a very old half-efficient washing machine at home? The laundry service downstairs has brand new machines that wash and dry your clothes way more quickly!

With these powerful machines, the “doing laundry” task gets a reduced execution time, so we can update our previous diagram:

We’re still running tasks in parallel, but thanks to the laundry mat’s resources, the laundry task is shorter!

We see that, now, it’s the dishwasher that takes the longest, which validates that the slowest task determines the total duration of the workflow in parallel execution.

The idea of delegating some of the work to better-equipped actors on the network is quite fashionable these days with the rise of cloud computing. Most of the big tech companies offer some on-demand computing service nowadays: Google’s Cloud Computing platform, Amazon’s AWS, IBM Cloud… There are multiple types of services but those typically follow the Platform as a service (PaaS) model. In other words, we first request for (and rent) a virtual machine on the company’s servers; then we can log in the virtual machine, set up the working environment we need and run our own programs; but we do not need to handle the underlying complexity of hardware or maintenance.

Basically: we get to access the nice machines from the laundry mat that are better than ours without having to maintain them or worry with their management.

Paying extra

Of course, in our capitalistic world, going to the laundry mat to wash your clothes means that you’ll need to pay for it. It’s the same for programmers: whenever you use an external service, there is a cost. Be it simply latency on the network (the laundry service is closed for another twenty minutes), an overload on the network (all machines are currently taken and you have to wait) or actual fees (the coin you put it the machine), calling upon someone else to take care of it asks for payment.

So, while using a remote procedure call can help you go the extra mile in terms of technology, support or resources, it also means you’ll pay a bit more for it.

Should you keep it to yourself?

Another point of concern when using external services is the protection of your identity and data. Let’s be honest: do you really want to have the entire neighborhood see your dirty laundry? What if the guy that oversees the laundry mat is known for going-and-telling the whole street about everyone’s little secrets? Would you feel at ease?

It’s the same with remote procedure call: since you agree to let another service handle the dirty work… you agree to let them take a look at your dirty laundry!

I would like to separate between two very different situations here: when you’re a programmer and you’re actually the one who wrote the “remote procedure”, and you’re just a user that (perhaps unknowingly) uses one.

First case scenario: you own the laundry service

So yeah, it still costs you in terms of energy or rented space, but you logically don’t spent a cent on actually doing your laundry. This coin you would put in the machine goes back to your pocket. Plus, there is less risk of leaking the information because you’re already at home, so you choose who enters and leaves and you can refuse to hire the gossipy guy in the corner. This means that you set up an entire business which probably cost you a lot at first, but now you can take advantage of it easily.

Second case scenario: it’s a public laundry service you might not know inside-and-out

If you’re more of a general public user and go on the web to convert some document into a PDF, you’re using remote procedure call. You’re basically delegating the conversion task to the online service. In these instances, security is very important. Most of the file-to-file conversion services promise to delete your uploaded files after a while, others supposedly don’t even store your data…

Besides those initial explicit rules (that depend on and define a philosophy), there is also the question of how the service is actually implemented, what technology it uses. Nowadays, there a dozens of tools and languages you can choose from to create an online web interface and the computation hidden behind it. Some are deemed safer than others, some are quicker, or more efficient when a lot of people use the website at the same time… in the end, it’s the whole trick from a programming standpoint: when writing a program and building the architecture around it, you need to pick the best tools for your case (to decide on a nice “tech stack”, as we say). Today there are basic standards that prevent programmers from ever going back to the dark ages and insure some minimal level of security; still, if you don’t tune them to your project properly, you can’t assure your customers their data is totally safe.

As users, it is important that we understand what it means to ask an external service to do something for us. We should always read the aforementioned “usage rules” and the overall philosophy of the online service, but it is crucial to understand that this is not the only layer of security and that we are still relying on the skills of the ones that coded the thing. Trusting their product is trusting them and their ability to provide what they advertized!

I’m not saying we shouldn’t use those services because plenty of them are very handy and lots of them are provided by thoughtful and good programmers. As usual, it’s more about staying vigilant, not blindly accepting every popup we see or not thoughtlessly giving our data to anyone (even services that don’t actually need it…).

To conclude

This was a high overview of a complex topic: there is a lot more to say about threading, distributed computing and network communication. In truth, each of these terms would deserve several articles fully devoted to it! (And I might actually write on some of those subjects in the future :))

Anyway, I had a lot of fun trying to find a nice analogy to describe some of the computer details. I voluntarily chose not to insist too much on the technical aspects – I do realize that domain experts will see big misstatements here. But it was really interesting for me to make an effort to popularize the topic and to really try and address the broadest audience possible.

En informatique, on aime bien exécuter des programmes. On écrit des lignes de code qui utilisent une certaine entrée, la donnent à un ensemble de rouages pour effectuer un calcul et recrachent un résultat. Ce n’est pas nouveau. Mais ce qui n’est pas nécessairement évident quand on débute en programmation, c’est la complexité qui se cache derrière cette phrase. Pour mieux comprendre, parlons de vaisselle, de shopping et de lessive.

J’ai récemment eu à réfléchir sur le sujet dans mon travail. Nous nous posions la question de “la sécurité versus la rapidité”. Globalement : imaginons que l’on ait un morceau de code qui puisse venir de n’importe où – faut-il simplement l’exécuter en priant pour que tout se passe bien ? ou bien faut-il mettre en place des gardes-fou pour éviter de casser notre ordinateur, de détruire des fichiers ou simplement de voir le programme s’arrêter et rester là, inutile, dans notre workflow ?

Quand on exécute un programme, c’est comme avec n’importe quel autre outil dans notre vie de tous les jours : il faut prendre en compte qui l’a fait pour comprendre comment il fonctionne, qui l’utilise et pourquoi, dans quel environnement il est utilisé… Tout cela m’a amenée à faire quelques recherches sur l’IPC (Inter-Processus Communication) et le RPC (Remote Procedure Call).Avertissement : pour les informaticiens : le RPC est techniquement de l’IPC également, puisqu’on y fait communiquer deux processus. Dans cet article, cependant, je considère l’IPC comme une communication strictement locale (et je distingue donc plutôt entre ce qui se passe sur la même machine et ce qui se passe à travers le réseau).

Prenons une analogie !

Supposons qu’on se prépare une journée assez fastidieuse : nous devons laver la vaisselle, faire quelques courses et nous occuper de la lessive. On peut considérer ces 3 tâches comme des petits programmes qui “transforment” chacun quelque chose en autre chose : laver la vaisselle nécessite de la vaisselle sale et en “produit” de la propre, les courses vont a priori convertir notre argent en du pain et des légumes et la lessive est similaire à la vaisselle, mais avec des vêtements.

Maintenant, faisons quelques hypothèses supplémentaires :

  • nous avons un lave-vaisselle, donc “laver la vaisselle” peut se traduire par : remplir le lave-vaisselle, préparer le programme de lavage, appuyer sur “start” et récupérer la vaisselle à la fin
  • nous avons une machine à laver (le linge) mais elle est vieille et décrépie – il lui faut deux fois plus de temps que la normale pour faire la lessive et certains vêtements ne sont pas complètement propres à la fin
  • d’un autre côté, il y a un lavomatic juste à côté
  • faire les courses nécessite que l’on soit là tout le long de l’opération, évidemment

Il y a une façon très simple – mais épuisante ! – de s’occuper de cette journée : on traite chaque problème l’un après l’autre en attendant que chaque processus soit entièrement fini pour le cocher sur notre liste. Autrement dit, on pourrait par exemple remplir le lave-vaisselle puis attendre patiemment à côté pendant qu’il travaille. Quelques heures plus tard, on rangerait les assiettes brillantes dans les placards et on s’attellerait à la tâche suivante. Au magasin, on remplirait notre panier en traversant les rayons, puis on irait à la caisse pour payer. Enfin, la lessive. Comme auparavant avec la vaisselle, on remplirait la vieille machine à laver, on attendrait encore plus longtemps et on aurait enfin nos vêtements à moitié lavés.

Voilà une représentation basique de toutes les corvées à faire si on décide de s’occuper de chacune l’une après l’autre :

Un aperçu des 3 corvées sur notre liste (laver la vaisselle, faire des courses, faire la lessive) avec les étapes intermédiaires pour chacune. Le fond rouge clair indique les étapes qui ne nécessitent pas réellement que nous soyons présents. La flèche bleue représente le cours du temps : ici, on effectue chaque étape l’une après l’autre.

On parle “d’exécution en série” car on exécute une série de tâches. On peut faire un petit diagramme pour voir comment cela va se dérouler dans le temps :

L’exécution en série, ou séquentielle, consiste à effectuer chaque tâche l’une après l’autre : la durée totale de la liste de corvées est la somme de la durée de chaque item.

On voit qu’en tout, il nous faudra environ 5 heures pour s’occuper de toute notre liste.

Vous avez sans doute remarqué maintenant que c’est un emploi du temps assez idiot. En particulier, certaines étapes comme “regarder le lave-vaisselle au travail” ne servent absolument à rien. A l’inverse, le temps passé au magasin a beau ne pas être la corvée la plus longue, il faut que nous soyons présents tout le long pour choisir les articles sur les rayonnages.

C’est la différence entre une tâche “synchrone” et une tâche “asynchrone”.

Tâches synchrones et asynchrones

Comme dans notre petit exemple, quand on souhaite exécuter deux programmes différents, on peut parfois se rendre compte que :

  • ces programmes n’ont pas vraiment besoin l’un de l’autre pour fonctionner (ici : “laver la vaisselle” et “faire les courses” ne sont pas liés, on peut les réaliser dans l’ordre que l’on veut, quand on veut, de la façon que l’on préfère)
  • certains peuvent être “lancés” et on peut ensuite faire autre chose jusqu’à ce qu’ils se terminent sans rester à les observer : ce sont des tâches asynchrones
  • d’autres à l’inverse nous demandent régulièrement d’interagir et on ne peut donc pas les laisser tranquilles : ce sont des tâches synchrones

Il y a bien sûr des avantages et des inconvénients aux deux.

Les tâches synchrones peuvent être chronophages car elles nous obligent à rester concentré sur la tâche en cours. En informatique, on parle d’instruction “bloquante” : certaines lignes de code qui sont synchrones peuvent prendre un certain temps à s’exécuter entièrement (par exemple : “faire les courses”) et jusqu’à ce que cette instruction soit terminée, nous sommes bloqués et nous ne pouvons pas passer à la tâche suivante dans la liste (l’instruction suivante dans notre/nos programme(s)). Le point positif, en revanche, c’est que quand l’instruction est (enfin) finie, on sait de manière certaine que le processus a transformé notre entrée en résultat de la forme attendue (bon, on peut avoir une erreur, ou une réponse fausse, mais ce qui est important est que la “transformation” est complète, ce n’est pas une sorte de monstre de Frankenstein dans un entre-deux à moitié terminé). Cela signifie que si tout s’est bien passé, on est totalement sûr de l’état de notre programme à ce moment précis.

D’un autre côté, les tâches asynchrones font le pari inverse : il faut être très attentif à quand on examine nos résultats (imaginons que le lave-vaisselle n’a pas terminé et qu’on prend les assiettes : elles ne sont pas encore bien nettoyées !) mais on n’a pas besoin de rester les surveiller jusqu’à ce qu’elles se terminent. Il y a de nombreux exemples de tâches asynchrones : les requêtes dans de grandes bases de données, des calculs mathématiques longs, le traitement de fichiers indépendants… Toutes ces situations ont lieu beaucoup plus souvent qu’on ne le croit au milieu des produits technologiques dont nous nous servons aujourd’hui : les moteurs de recherche, les réseaux sociaux, les magasins en ligne, etc. L’idée de base derrière la programmation asynchrone est que, plutôt que d’attendre que la tâche soit terminée en la regardant nous-mêmes, on attend que la tâche nous prévienne quand elle est finie. De cette façon, on peut travailler sur autre chose, oublier complètement notre tâche asynchrone et recevoir une sorte de notification quand la tâche est terminée. C’est ce qu’il se passe quand notre lave-vaisselle sonne à la fin de son programme de lavage : il nous prévient que son travail est fini pour que l’on puisse incorporer cette information au reste et agir en conséquence.

Une dernière remarque importante à propos de nos deux tâches “laver la vaisselle” et “faire les courses” : comme mentionné auparavant, il n’y a pas de lien entre les deux. Elles n’utilisent pas les mêmes données au départ et ne dépendent pas l’une de l’autre. Cela signifie que, dans un monde idéal où on aurait le don d’ubiquité, on pourrait s’occuper des deux en même temps sans problème. En langage informatique, on dit qu’elles peuvent être parallélisées (car elles ont des “lignes de vie parallèles”).

Si on compare à l’exécution sérielle présentée tout à l’heure, l’exécution parallèle peut être représentée ainsi :

L’exécution parallèle nous permet de gérer les tâches indépendantes en parallèle pour ne pas perdre du temps inutilement. La durée totale de liste des corvées est la durée de la tâche la plus lente (en l’occurrence, faire la lessive avec notre vieille machine à laver).

Note : ici, j’indique “local” dans le titre car nous allons voir bientôt les différences entre l’exécution locale (IPC) et l’exécution distante (RPC) et en quoi cela affecte la durée totale de nos corvées.

On remarque qu’en “regroupant” les tâches, on gagne près de 3 heures de temps libre par rapport à notre mode “complètement séquentiel” précédent ! C’est généralement le compromis que l’on veut faire en informatique quand on utilise une exécution parallèle : on consacre un peu plus de temps à écrire le code initial et un peu plus de resources pendant l’exécution pour pouvoir obtenir une importante réduction du temps d’exécution en retour.

Même si ce n’est pas évident, pour moi, un bon programmeur doit toujours essayer d’identifier ce qui peut ou non être asynchrone pour profiter au mieux des outils à sa disposition dans le domaine. Car il y en a beaucoup, des outils ! Au fil des années, les ingénieurs ont développé d’incroyables composants et logiciels qui ont peu à peu amélioré notre capacité à paralléliser des tâches.

Travailler en solo : l’IPC (local)

On pourrait penser qu’avoir un seul ordinateur signifie que l’on ne peut exécuter qu’une tâche à la fois. Mais ce n’est plus le cas ! C’était effectivement vrai par le passé, mais aujourd’hui nous avons différents moyens de passer outre cette limitation à l’aide des threads ou des GPUs par exemple.

Comme un chef d’orchestre à l’opéra…

Dans un ordinateur, les différentes tâches actuellement planifiées sont appelées des “processus”. Ici, on aurait donc un processus correspondant à la vaisselle, un aux courses et un troisième à la lessive. Chaque processus est composé d’un ensemble d’instructions et d’un contexte d’exécution ; dans notre cas, par exemple, “laver la vaisselle” peut être divisé en :

  • une liste d’instructions : remplir le lave-vaisselle, préparer le programme de lavage, appuyer sur le bouton “start” et récupérer la vaiselle propre
  • un contexte : la vaisselle que nous devons nettoyer cette fois, est-ce que nous avons déjà préparé le programme de lavage ou non…

On peut voir que, de manière générale, la liste d’instructions est fixe (c’est le programme que nous avons préparé et il contient les mêmes données à chaque fois) tandis que le contexte est plus volatile : il évolue à mesure que le processus s’exécute et il peut contenir des données “dynamiques”, c’est-à-dire des données qui dépendent du moment où on exécute le processus. On pourrait avoir beaucoup d’assiettes à laver un jour mais par contre des couverts le jour suivant.

A l’intérieur d’un ordinateur, le système d’exploitation, ou operating system – OS – (Windows, Linux, Mac OS X…) est le chef d’un grand orchestre. Entre autres choses, il doit gérer les processus actuellement planifiés pour s’assurer que chacun a assez de resources pour être réalisé correctement. Le problème, c’est qu’un ordinateur est intrinsèquement séquentiel ; en réalité, il ne peut pas faire du multi-tâches. Nos architectures modernes sont cependant capables de simuler exceptionnellement bien le multi-tâches.

La clé, c’est de passer d’un processus à l’autre très rapidement pour que chacun ait droit à une petite part du gâteau régulièrement et continue à effectuer sa liste d’instructions jusqu’à ce qu’il soit terminé. Si on veut avoir l’impression de progresser dans toutes nos corvées, on peut sauter de l’une à l’autre pour exécuter une petite étape dans chacune, puis passer à la suivante et refaire le tour…

En reprenant notre petit diagramme des tâches, voilà comment il serait modifié :

Contrairement à l’exécution séquentielle, on n’attend plus qu’un to-do soit complètement terminé avant de s’attaquer au suivant. On se souvient plutôt de l’endroit où on s’était arrêté la dernière fois et on fait progresser toutes les tâches en même temps. On voit néanmoins qu’il y a une latence entre chaque étape car il nous faut un peu de temps pour passer d’une tâche à l’autre. La problématique est donc : comment peut-on passer d’une tâche à l’autre suffisamment vite pour obtenir quelque chose qui ressemble à du multi-tâches ?

Note : dans un ordinateur, “se souvenir du dernier arrêt” se fait en sauvegardant ce que l’on appelle un pointeur d’instruction dans le contexte du processus. Quand l’OS décide que c’est au tour de ce processus parallèle de mener la danse (pour un court moment, évidemment), il restaure le contexte et en particulier il retourne à la dernière instruction exécutée à l’aide de ce pointeur.

Il y a de nombreuses façons de faire de l’exécution parallèle, les plus communes étant les threads et les GPUs. Ces nouvelles solutions répondent à notre problème de changement de processus en donnant des temps de transition très faibles. Les threads sont dits “légers” car les récupérer pour passer à l’étape suivante se fait très rapidement. Grâce à ces technologies, on peut réduire le temps de transition jusqu’à avoir l’impression d’avoir du multi-tâches :

Et nous avons enfin une exécution parallèle !

Le terme d’IPC (inter-process communication en anglais, ou communication inter-processus) fait référence au fait que, de nos jours, on peut avoir des interactions entre nos processus, échanger des données…

Déléguer à autrui : le RPC

Jusqu’ici, nous nous sommes concentrés sur une solution locale, i.e. une technologie qui n’induit pas de communiquer entre plusieurs ordinateurs à travers un réseau. Dans notre analogie, cela correspond au fait que nous avons utilisé notre lave-vaisselle et notre machine à laver personnels pour nous occuper des corvées.

Une autre possibilité est de faire de l’exécution à distance à l’aide d’appels de processus distants (remote procedure calls, RPC) pour avoir plusieurs processus répartis sur plusieurs ordinateurs qui communiquent ensemble (avec de la communication inter-processus non locale).

Tirer profit de meilleures ressources

Vous souvenez-vous de notre vieille machine à laver, si peu efficace ? Le lavomatic en bas a de toutes nouvelles machines qui nettoient et sèchent les vêtements beaucoup plus vite !

Avec ces machines plus puissantes, la tâche de “faire la lessive” se fait plus vite, alors nous pouvons mettre à jour notre schéma précédent :

On effectue toujours nos tâches en parallèle mais grâce aux ressources du lavomatic, faire la lessive est beaucoup plus rapide !

On voit que, maintenant, c’est la vaisselle qui est la tâche la plus longue, ce qui valide le fait que c’est la tâche la plus longue qui détermine la durée totale de travail dans une exécution parallèle.

L’idée de déléguer une partie du travail à des acteurs mieux équipés sur le réseau est assez d’actualité avec l’essor du cloud computing. La plupart des grandes entreprises de la tech offrent maintenant des services de calcul à la demande : Google a sa plateforme Google Cloud Computing, Amazon a AWS, IBM a IBM Cloud… Il y a de nombreux types de services mais ils suivent généralement le modèle de Platform as a service (PaaS). Autrement dit, on demande (et on loue) une machine virtuelle sur les serveurs de l’entreprise ; on peut ensuite s’y connecter, mettre en place l’environnement de travail dont on a besoin et y exécuter nos propres programmes ; mais on n’a pas à se soucier de la complexité physique de l’installation ou de la maintenance.

En gros : on a un accès à de belles machines au lavomatic qui sont bien meilleures que la nôtre sans avoir à en faire la maintenance ou la gestion.

Une facture plus salée

Bien sûr, dans notre monde capitaliste, aller au lavomatic pour faire sa lessive nécessite de payer. C’est la même chose en programmation : lorsque l’on utilise un service externe, il y a un coût. Que ce soit simplement la latence sur le réseau (le lavomatic ne rouvre que dans 20 minutes), une surcharge du réseau (toutes les machines sont occupées et il faut attendre) ou un prix immédiat (les pièces à mettre dans la machine), demander à quelqu’un de s’en occuper pour nous requiert un paiement.

Donc, même si faire une exécution distance peut nous aider en terme de technologie, de support ou de ressources, cela signifie aussi qu’il faudra payer plus pour récupérer nos résultats.

Faut-il garder ses infos secrètes ?

Un autre sujet de préoccupation quand on utilise des services externes est la protection de notre identité et de nos données. Honnêtement : auriez-vous vraiment envie que tout le quartier voit votre linge sale ? Et si l’employé qui gère le lavomatic est connu pour être une commère qui raconte à toute la rue les secrets de tout le monde ? Est-ce que vous vous sentiriez à l’aise ?

C’est la même chose avec une exécution à distance : comme on accepte qu’un autre service s’occupe du sale boulot… on accepte aussi qu’il jette un oeil à notre linge sale !

Ici, je tiens à séparer entre deux situations très différentes : quand on est un programmeur et qu’on est en fait celui qui a écrit le “processus distant”, et quand on est juste un utilisateur qui s’en sert (peut-être sans en avoir conscience).

1er cas : on possède le lavomatic

Bon, oui, ça nous coûte en terme d’énergie ou d’espace loué, mais on ne dépense logiquement pas un centime pour faire notre lessive. La pièce qu’on mettrait dans la machine revient directement dans notre poche. Et puis, il y a moins de risque que l’information fuite car on est déjà chez nous, donc on peut choisir qui entre et sort et on peut refuser d’engager la commère dans le coin. Cela signifie que l’on a dû mettre en place tout en business, ce qui était probablement coûteux au départ, mais qu’on peut maintenant en profiter facilement.

2e cas: c’est un lavomatic public dont on ne connaît pas nécessairement tous les secrets

Si on est quelqu’un du grand public et qu’on va sur internet pour convertir un document en PDF, on se sert d’un RPC. On délègue en fait la tâche de conversion au service en ligne. Dans ces cas-là, la sécurité est primordiale. La plupart des services de conversion de fichier promettent de détruire les fichiers que vous avez uploadés après un moment, d’autres prétendent ne même pas stocker vos données…

En plus de ces règles explicites initiales (qui dépendent de et définissent une philosophie), il y a aussi le problème de l’implémentation réelle du service, de la technologie qu’il utilise. A l’heure actuelle, on peut choisir parmi des dizaines d’outils et de langages pour créer une interface de site web et effectuer les calculs dissimulés derrière. Certains sont réputés plus sûrs que d’autres, certains sont plus rapides, ou plus efficaces quand il y a beaucoup d’utilisateurs en même temps… au final, c’est là toute la ruse du point de vue du programmeur : quand on écrit un programme et que l’on construit une architecture autour, il faut choisir les meilleurs outils pour notre situation (on dit qu’il faut choisir la meilleure “stack de technologies”). Aujourd’hui, il existe des standards de base qui empêchent les programmeurs de retomber au Moyen-Âge et assurent un niveau de sécurité minimal ; mais si on ne les adapte pas proprement à notre projet, on ne peut garantir à nos utilisateurs que leurs données sont bien sécurisées.

En tant qu’utilisateurs, il est important de comprendre ce qu’implique l’utilisation d’un service externe. Il faut toujours bien lire les règles d’usage et la philosophie du service en ligne, mais il est aussi crucial de comprendre que ce n’est pas la seule couche de sécurité et que nous nous reposons tout de même sur les compétences de ceux qui l’ont codé. Faire confiance à leur produit, c’est leur faire confiance à eux et à leur capacité à fournir ce qu’ils nous ont vendu !

Je ne dis pas qu’il ne faut pas utiliser ces services, car beaucoup sont très pratiques et bon nombre d’entre eux sont fournis par des programmeurs attentionnés et plein de bonnes intentions. Comme d’habitude, il s’agit plutôt de rester aux aguets et de ne pas accepter tous les popups ou de ne pas fournir toutes nos données sans réfléchir (même à des services qui n’en ont pas réellement besoin…).

Pour conclure

Cet article était un aperçu rapide d’un sujet complexe : il y a beaucoup à dire sur le threading, le calcul distribué et la communication réseau. En réalité, chacun de ces termes mériterait plusieurs articles ! (Et peut-être que je traiterai certains de ces sujets dans le futur :))

Je me suis en tout cas bien amusée à chercher une bonne analogie pour décrire certains des détails informatiques. J’ai volontairement décidé de ne pas trop insister sur les aspects techniques – j’ai bien conscience que des spécialistes du domaine doivent voir ici de grosses inexactitudes. Mais il était intéressant pour moi d’avoir une démarche de vulgarisateur et de vraiment chercher à m’adresser au public le plus large possible.

  1. randu.org’s tutorial on POSIX threads: https://randu.org/tutorials/threads/
  2. A definition of the word “GPU”: https://techterms.com/definition/gpu
  3. M. James, “What Is Asynchronous Programming?” (https://www.i-programmer.info/programming/theory/6040-what-is-asynchronous-programming.html), June 2013. [Online; last access 14-June-2020].
  4. I. Wikimedia Foundation, “Inter-process communication” (https://en.wikipedia.org/wiki/Inter-process_communication), May 2020. [Online; last access14-June-2020].
  5. I. Wikimedia Foundation, “Remote Procedure Call” (https://en.wikipedia.org/wiki/Remote_procedure_call), June 2020. [Online; last access 14-June-2020].
  6. I. Wikimedia Foundation, “Parallel computing” (https://en.wikipedia.org/wiki/Parallel_computing), June 2020. [Online; last access 14-June-2020].

Leave a Reply

Your email address will not be published. Required fields are marked *