J'ai une petite question concernant la gestion de la concurrence.
J'ai un programme avec des threads "producer" et "consumer".
Je me sert d'une "blockingqueue" pour comminiquer.
au départ, j'arrivais à ajuster le nombre de producer/consumers pour que le consumer dépile la queue assez vite.
mais maintenant que je suis en prod, je suis sur un serveur mega puissant (avec beaucoup de core).
même si j'ai un seul thread producer, et le reste des cores pour le consumer, ils n'arrivent pas à dépiler.
le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a pas traité un message (doublons).
pour ça, je fait un truc moche qui bloque tous les threads: un lock sur un HashSet.
j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true" c'est qu'il
n'existait pas déjà, false dans le cas contraire.
du coup, tous les threads bloquent pour vérifier si l'item n'est pas déjà dans la hashset...
avez vous des idées pour eviter ce lock?
une idée que j'ai c'est de "router" les messages dans des blocking queue différentes (1 par thread consumer) le routing se fait à l'aide d'un hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà traité...
je garantie ansi que les doublons vont dans la même queue, que l'unicité est donc garantie, mais je répartie les locks par thread...
Idée : decorreler dédoublonnage et traitement.
Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
dans une autre Q pour les consumers.
Make sense ?
Mathieu
Le 9 août 2012 à 20:46, Mathias Kluba <mathias.kl...@gmail.com> a écrit :
> J'ai une petite question concernant la gestion de la concurrence.
> J'ai un programme avec des threads "producer" et "consumer".
> Je me sert d'une "blockingqueue" pour comminiquer.
> au départ, j'arrivais à ajuster le nombre de producer/consumers pour que le consumer dépile la queue assez vite.
> mais maintenant que je suis en prod, je suis sur un serveur mega puissant (avec beaucoup de core).
> même si j'ai un seul thread producer, et le reste des cores pour le consumer, ils n'arrivent pas à dépiler.
> le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a pas traité un message (doublons).
> pour ça, je fait un truc moche qui bloque tous les threads: un lock sur un HashSet.
> j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true" c'est qu'il
> n'existait pas déjà, false dans le cas contraire.
> du coup, tous les threads bloquent pour vérifier si l'item n'est pas déjà dans la hashset...
> avez vous des idées pour eviter ce lock?
> une idée que j'ai c'est de "router" les messages dans des blocking queue différentes (1 par thread consumer) le routing se fait à l'aide d'un hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà traité...
> je garantie ansi que les doublons vont dans la même queue, que l'unicité est donc garantie, mais je répartie les locks par thread...
Ou alors tu fais porter l'info de traitement au message. En gros tu ajoutes
à ton message deux booleens (volatiles) et une date : en cours de
traitement, traité, date de début de traitement (et un object de synchro)
Tu as une queue avec tous les messages qui ne sont pas en cours de
traitement et une avec les messages en cours de traitement (important pour
la reprise sur erreur)
Ton début de traitement d'un message revient à faire
var msg = queue.take(); // au pire tu as deux ou trois threads qui prennent
le même message
lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais
tu n'es pas obligé de faire pareil
{
if(!msg.Processing && !msg.Processed)
{
msg.Processing = true;
msg.StartProcessingDate = DateTime.Now; // à voir si la précision est
suffisante
processingQueue.Put(msg);
process (msg);
}
}
Quand ta queue de départ est vide, tu vas chercher dans la processingQueue
les message qui ne sont pas déjà traités et dont la date de début est
antérieure à x minutes, histoire de pallier aux défaillance de tes
consumers.
Avantage : tu lockes au niveau du message seulement, donc dans le pire des
cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même
temps du même message)
Mick, distribué.
Le 9 août 2012 21:35, Mathieu Despriee <mdespr...@octo.com> a écrit :
> Idée : decorreler dédoublonnage et traitement.
> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
> dans une autre Q pour les consumers.
> Make sense ?
> Mathieu
> Le 9 août 2012 à 20:46, Mathias Kluba <mathias.kl...@gmail.com> a écrit :
> > Hello,
> > J'ai une petite question concernant la gestion de la concurrence.
> > J'ai un programme avec des threads "producer" et "consumer".
> > Je me sert d'une "blockingqueue" pour comminiquer.
> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour que
> le consumer dépile la queue assez vite.
> > mais maintenant que je suis en prod, je suis sur un serveur mega
> puissant (avec beaucoup de core).
> > même si j'ai un seul thread producer, et le reste des cores pour le
> consumer, ils n'arrivent pas à dépiler.
> > le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a
> pas traité un message (doublons).
> > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur
> un HashSet.
> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true"
> c'est qu'il
> > n'existait pas déjà, false dans le cas contraire.
> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas
> déjà dans la hashset...
> > avez vous des idées pour eviter ce lock?
> > une idée que j'ai c'est de "router" les messages dans des blocking queue
> différentes (1 par thread consumer) le routing se fait à l'aide d'un hash
> sur l'id du message. et chaque thread possède sa Hashset d'id déjà traité...
> > je garantie ansi que les doublons vont dans la même queue, que l'unicité
> est donc garantie, mais je répartie les locks par thread...
Tout d'abord, j'ai plusieurs queues.
Donc je décolère déjà traitement et "détection des doublons".
Avant je n'avais pas de problème car les traitements prenaient pas mal de CPU.
Mais quand je suis passé en prod avec un serveur de ouf, les traitements vont trop vite et tout bloque sur la détection des doublons.
message => queue 1 => traitements A => queue 2 => détection des doublons => queue 3 => traitements B
le problème c'est que le "traitements A" va tellement vite, que la queue 2 est pleine (capé sinon ça explose en mémoire).
et la queue3 est vide, car la "détection des doublons" va pas assez vite pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
Concernant le lock au niveau du message: mes threads ne peuvent pas dépiler le même message (blockingQueue).
J'ai des doublons au niveau de la source des messages.
désolé de ne pas avoir été assez détaillé dans mon premier message, j'étais sur iPhone dans le metro :p
PS: je sais que ma solution de "blocking queue" n'est pas idéal: je pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
> Ou alors tu fais porter l'info de traitement au message. En gros tu > ajoutes à ton message deux booleens (volatiles) et une date : en cours de > traitement, traité, date de début de traitement (et un object de synchro)
> Tu as une queue avec tous les messages qui ne sont pas en cours de > traitement et une avec les messages en cours de traitement (important pour > la reprise sur erreur)
> Ton début de traitement d'un message revient à faire
> var msg = queue.take(); // au pire tu as deux ou trois threads qui > prennent le même message
> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais > tu n'es pas obligé de faire pareil
> {
> if(!msg.Processing && !msg.Processed)
> {
> msg.Processing = true;
> msg.StartProcessingDate = DateTime.Now; // à voir si la précision est > suffisante
> processingQueue.Put(msg);
> process (msg);
> }
> }
> Quand ta queue de départ est vide, tu vas chercher dans la processingQueue > les message qui ne sont pas déjà traités et dont la date de début est > antérieure à x minutes, histoire de pallier aux défaillance de tes > consumers.
> Avantage : tu lockes au niveau du message seulement, donc dans le pire des > cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même > temps du même message)
> Mick, distribué.
> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com <javascript:>> a > écrit :
>> Salut Mathias,
>> Idée : decorreler dédoublonnage et traitement.
>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>> dans une autre Q pour les consumers.
>> Make sense ?
>> Mathieu
>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com <javascript:>> >> a écrit :
>> > Hello,
>> > J'ai une petite question concernant la gestion de la concurrence.
>> > J'ai un programme avec des threads "producer" et "consumer".
>> > Je me sert d'une "blockingqueue" pour comminiquer.
>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour >> que le consumer dépile la queue assez vite.
>> > mais maintenant que je suis en prod, je suis sur un serveur mega >> puissant (avec beaucoup de core).
>> > même si j'ai un seul thread producer, et le reste des cores pour le >> consumer, ils n'arrivent pas à dépiler.
>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il >> n'a pas traité un message (doublons).
>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur >> un HashSet.
>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit >> "true" c'est qu'il
>> > n'existait pas déjà, false dans le cas contraire.
>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas >> déjà dans la hashset...
>> > avez vous des idées pour eviter ce lock?
>> > une idée que j'ai c'est de "router" les messages dans des blocking >> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un >> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà >> traité...
>> > je garantie ansi que les doublons vont dans la même queue, que >> l'unicité est donc garantie, mais je répartie les locks par thread...
Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le
traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut
pas être lui qui a la hashmap et qui dedoublonne ?
Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a écrit :
Tout d'abord, j'ai plusieurs queues.
Donc je décolère déjà traitement et "détection des doublons".
Avant je n'avais pas de problème car les traitements prenaient pas mal de
CPU.
Mais quand je suis passé en prod avec un serveur de ouf, les traitements
vont trop vite et tout bloque sur la détection des doublons.
message => queue 1 => traitements A => queue 2 => détection des doublons
=> queue 3 => traitements B
le problème c'est que le "traitements A" va tellement vite, que la queue 2
est pleine (capé sinon ça explose en mémoire).
et la queue3 est vide, car la "détection des doublons" va pas assez vite
pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
Concernant le lock au niveau du message: mes threads ne peuvent pas dépiler
le même message (blockingQueue).
J'ai des doublons au niveau de la source des messages.
désolé de ne pas avoir été assez détaillé dans mon premier message, j'étais
sur iPhone dans le metro :p
PS: je sais que ma solution de "blocking queue" n'est pas idéal: je
pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
> Ou alors tu fais porter l'info de traitement au message. En gros tu
> ajoutes à ton message deux booleens (volatiles) et une date : en cours de
> traitement, traité, date de début de traitement (et un object de synchro)
> Tu as une queue avec tous les messages qui ne sont pas en cours de
> traitement et une avec les messages en cours de traitement (important pour
> la reprise sur erreur)
> Ton début de traitement d'un message revient à faire
> var msg = queue.take(); // au pire tu as deux ou trois threads qui
> prennent le même message
> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais
> tu n'es pas obligé de faire pareil
> {
> if(!msg.Processing && !msg.Processed)
> {
> msg.Processing = true;
> msg.StartProcessingDate = DateTime.Now; // à voir si la précision est
> suffisante
> processingQueue.Put(msg);
> process (msg);
> }
> }
> Quand ta queue de départ est vide, tu vas chercher dans la processingQueue
> les message qui ne sont pas déjà traités et dont la date de début est
> antérieure à x minutes, histoire de pallier aux défaillance de tes
> consumers.
> Avantage : tu lockes au niveau du message seulement, donc dans le pire des
> cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même
> temps du même message)
> Mick, distribué.
> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com <javascript:>> a
> écrit :
>> Salut Mathias,
>> Idée : decorreler dédoublonnage et traitement.
>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>> dans une autre Q pour les consumers.
>> Make sense ?
>> Mathieu
>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com <javascript:>>
>> a écrit :
>> > Hello,
>> > J'ai une petite question concernant la gestion de la concurrence.
>> > J'ai un programme avec des threads "producer" et "consumer".
>> > Je me sert d'une "blockingqueue" pour comminiquer.
>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour
>> que le consumer dépile la queue assez vite.
>> > mais maintenant que je suis en prod, je suis sur un serveur mega
>> puissant (avec beaucoup de core).
>> > même si j'ai un seul thread producer, et le reste des cores pour le
>> consumer, ils n'arrivent pas à dépiler.
>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il
>> n'a pas traité un message (doublons).
>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur
>> un HashSet.
>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit
>> "true" c'est qu'il
>> > n'existait pas déjà, false dans le cas contraire.
>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas
>> déjà dans la hashset...
>> > avez vous des idées pour eviter ce lock?
>> > une idée que j'ai c'est de "router" les messages dans des blocking
>> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un
>> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà
>> traité...
>> > je garantie ansi que les doublons vont dans la même queue, que
>> l'unicité est donc garantie, mais je répartie les locks par thread...
Arf, non, car la traitement A génère le messages à partir d'un fichier
texte, et génère l'ID aussi.
Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement A,
pour traiter encore plus de fichiers (ce qui ne sert à rien pour l'instant
car ça bloque au dédoublonnage)
> Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le
> traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut
> pas être lui qui a la hashmap et qui dedoublonne ?
> Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a écrit :
> Tout d'abord, j'ai plusieurs queues.
> Donc je décolère déjà traitement et "détection des doublons".
> Avant je n'avais pas de problème car les traitements prenaient pas mal de
> CPU.
> Mais quand je suis passé en prod avec un serveur de ouf, les traitements
> vont trop vite et tout bloque sur la détection des doublons.
> message => queue 1 => traitements A => queue 2 => détection des doublons
> => queue 3 => traitements B
> le problème c'est que le "traitements A" va tellement vite, que la queue 2
> est pleine (capé sinon ça explose en mémoire).
> et la queue3 est vide, car la "détection des doublons" va pas assez vite
> pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
> Concernant le lock au niveau du message: mes threads ne peuvent pas
> dépiler le même message (blockingQueue).
> J'ai des doublons au niveau de la source des messages.
> désolé de ne pas avoir été assez détaillé dans mon premier message,
> j'étais sur iPhone dans le metro :p
> PS: je sais que ma solution de "blocking queue" n'est pas idéal: je
> pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
> Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
>> Ou alors tu fais porter l'info de traitement au message. En gros tu
>> ajoutes à ton message deux booleens (volatiles) et une date : en cours de
>> traitement, traité, date de début de traitement (et un object de synchro)
>> Tu as une queue avec tous les messages qui ne sont pas en cours de
>> traitement et une avec les messages en cours de traitement (important pour
>> la reprise sur erreur)
>> Ton début de traitement d'un message revient à faire
>> var msg = queue.take(); // au pire tu as deux ou trois threads qui
>> prennent le même message
>> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais
>> tu n'es pas obligé de faire pareil
>> {
>> if(!msg.Processing && !msg.Processed)
>> {
>> msg.Processing = true;
>> msg.StartProcessingDate = DateTime.Now; // à voir si la précision est
>> suffisante
>> processingQueue.Put(msg);
>> process (msg);
>> }
>> }
>> Quand ta queue de départ est vide, tu vas chercher dans la
>> processingQueue les message qui ne sont pas déjà traités et dont la date de
>> début est antérieure à x minutes, histoire de pallier aux défaillance de
>> tes consumers.
>> Avantage : tu lockes au niveau du message seulement, donc dans le pire
>> des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en
>> même temps du même message)
>> Mick, distribué.
>> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
>>> Salut Mathias,
>>> Idée : decorreler dédoublonnage et traitement.
>>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>>> dans une autre Q pour les consumers.
>>> Make sense ?
>>> Mathieu
>>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
>>> > Hello,
>>> > J'ai une petite question concernant la gestion de la concurrence.
>>> > J'ai un programme avec des threads "producer" et "consumer".
>>> > Je me sert d'une "blockingqueue" pour comminiquer.
>>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour
>>> que le consumer dépile la queue assez vite.
>>> > mais maintenant que je suis en prod, je suis sur un serveur mega
>>> puissant (avec beaucoup de core).
>>> > même si j'ai un seul thread producer, et le reste des cores pour le
>>> consumer, ils n'arrivent pas à dépiler.
>>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il
>>> n'a pas traité un message (doublons).
>>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock
>>> sur un HashSet.
>>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit
>>> "true" c'est qu'il
>>> > n'existait pas déjà, false dans le cas contraire.
>>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas
>>> déjà dans la hashset...
>>> > avez vous des idées pour eviter ce lock?
>>> > une idée que j'ai c'est de "router" les messages dans des blocking
>>> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un
>>> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà
>>> traité...
>>> > je garantie ansi que les doublons vont dans la même queue, que
>>> l'unicité est donc garantie, mais je répartie les locks par thread...
Question :: Est-ce une catastrophe si un message est traité en double de
temps à autre où pas ?
Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne
locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock).
Certains messages seront traités en double, mais bon, c'est pas la mort.
Si en revanche traiter deux fois le même message relève de la catastrophe
nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide
que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom,
mais en gros c'est un champ de bits dans lequel on insère des infos. C'est
destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet
de tester l'existence avec un simple ou binaire. J'essaie de te retrouver
le nom du truc, sauf si quelqu'un le connait de tête.
* du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta
complexité tend vers O(1)
Mick
Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com> a écrit :
> Arf, non, car la traitement A génère le messages à partir d'un fichier
> texte, et génère l'ID aussi.
> Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
> L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement
> A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour
> l'instant car ça bloque au dédoublonnage)
>> Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le
>> traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut
>> pas être lui qui a la hashmap et qui dedoublonne ?
>> Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a écrit :
>> Tout d'abord, j'ai plusieurs queues.
>> Donc je décolère déjà traitement et "détection des doublons".
>> Avant je n'avais pas de problème car les traitements prenaient pas mal de
>> CPU.
>> Mais quand je suis passé en prod avec un serveur de ouf, les traitements
>> vont trop vite et tout bloque sur la détection des doublons.
>> message => queue 1 => traitements A => queue 2 => détection des doublons
>> => queue 3 => traitements B
>> le problème c'est que le "traitements A" va tellement vite, que la queue
>> 2 est pleine (capé sinon ça explose en mémoire).
>> et la queue3 est vide, car la "détection des doublons" va pas assez vite
>> pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
>> Concernant le lock au niveau du message: mes threads ne peuvent pas
>> dépiler le même message (blockingQueue).
>> J'ai des doublons au niveau de la source des messages.
>> désolé de ne pas avoir été assez détaillé dans mon premier message,
>> j'étais sur iPhone dans le metro :p
>> PS: je sais que ma solution de "blocking queue" n'est pas idéal: je
>> pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
>> Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
>>> Ou alors tu fais porter l'info de traitement au message. En gros tu
>>> ajoutes à ton message deux booleens (volatiles) et une date : en cours de
>>> traitement, traité, date de début de traitement (et un object de synchro)
>>> Tu as une queue avec tous les messages qui ne sont pas en cours de
>>> traitement et une avec les messages en cours de traitement (important pour
>>> la reprise sur erreur)
>>> Ton début de traitement d'un message revient à faire
>>> var msg = queue.take(); // au pire tu as deux ou trois threads qui
>>> prennent le même message
>>> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync,
>>> mais tu n'es pas obligé de faire pareil
>>> {
>>> if(!msg.Processing && !msg.Processed)
>>> {
>>> msg.Processing = true;
>>> msg.StartProcessingDate = DateTime.Now; // à voir si la précision
>>> est suffisante
>>> processingQueue.Put(msg);
>>> process (msg);
>>> }
>>> }
>>> Quand ta queue de départ est vide, tu vas chercher dans la
>>> processingQueue les message qui ne sont pas déjà traités et dont la date de
>>> début est antérieure à x minutes, histoire de pallier aux défaillance de
>>> tes consumers.
>>> Avantage : tu lockes au niveau du message seulement, donc dans le pire
>>> des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en
>>> même temps du même message)
>>> Mick, distribué.
>>> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
>>>> Salut Mathias,
>>>> Idée : decorreler dédoublonnage et traitement.
>>>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>>>> dans une autre Q pour les consumers.
>>>> Make sense ?
>>>> Mathieu
>>>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
>>>> > Hello,
>>>> > J'ai une petite question concernant la gestion de la concurrence.
>>>> > J'ai un programme avec des threads "producer" et "consumer".
>>>> > Je me sert d'une "blockingqueue" pour comminiquer.
>>>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour
>>>> que le consumer dépile la queue assez vite.
>>>> > mais maintenant que je suis en prod, je suis sur un serveur mega
>>>> puissant (avec beaucoup de core).
>>>> > même si j'ai un seul thread producer, et le reste des cores pour le
>>>> consumer, ils n'arrivent pas à dépiler.
>>>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il
>>>> n'a pas traité un message (doublons).
>>>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock
>>>> sur un HashSet.
>>>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit
>>>> "true" c'est qu'il
>>>> > n'existait pas déjà, false dans le cas contraire.
>>>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas
>>>> déjà dans la hashset...
>>>> > avez vous des idées pour eviter ce lock?
>>>> > une idée que j'ai c'est de "router" les messages dans des blocking
>>>> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un
>>>> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà
>>>> traité...
>>>> > je garantie ansi que les doublons vont dans la même queue, que
>>>> l'unicité est donc garantie, mais je répartie les locks par thread...
> Question :: Est-ce une catastrophe si un message est traité en double de
> temps à autre où pas ?
> Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne
> locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock).
> Certains messages seront traités en double, mais bon, c'est pas la mort.
> Si en revanche traiter deux fois le même message relève de la catastrophe
> nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide
> que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom,
> mais en gros c'est un champ de bits dans lequel on insère des infos. C'est
> destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet
> de tester l'existence avec un simple ou binaire. J'essaie de te retrouver
> le nom du truc, sauf si quelqu'un le connait de tête.
> * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta
> complexité tend vers O(1)
> Mick
> Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com> a écrit :
> Arf, non, car la traitement A génère le messages à partir d'un fichier
>> texte, et génère l'ID aussi.
>> Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
>> L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement
>> A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour
>> l'instant car ça bloque au dédoublonnage)
>>> Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le
>>> traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut
>>> pas être lui qui a la hashmap et qui dedoublonne ?
>>> Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a
>>> écrit :
>>> Tout d'abord, j'ai plusieurs queues.
>>> Donc je décolère déjà traitement et "détection des doublons".
>>> Avant je n'avais pas de problème car les traitements prenaient pas mal
>>> de CPU.
>>> Mais quand je suis passé en prod avec un serveur de ouf, les traitements
>>> vont trop vite et tout bloque sur la détection des doublons.
>>> message => queue 1 => traitements A => queue 2 => détection des
>>> doublons => queue 3 => traitements B
>>> le problème c'est que le "traitements A" va tellement vite, que la queue
>>> 2 est pleine (capé sinon ça explose en mémoire).
>>> et la queue3 est vide, car la "détection des doublons" va pas assez vite
>>> pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
>>> Concernant le lock au niveau du message: mes threads ne peuvent pas
>>> dépiler le même message (blockingQueue).
>>> J'ai des doublons au niveau de la source des messages.
>>> désolé de ne pas avoir été assez détaillé dans mon premier message,
>>> j'étais sur iPhone dans le metro :p
>>> PS: je sais que ma solution de "blocking queue" n'est pas idéal: je
>>> pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
>>> Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
>>>> Ou alors tu fais porter l'info de traitement au message. En gros tu
>>>> ajoutes à ton message deux booleens (volatiles) et une date : en cours de
>>>> traitement, traité, date de début de traitement (et un object de synchro)
>>>> Tu as une queue avec tous les messages qui ne sont pas en cours de
>>>> traitement et une avec les messages en cours de traitement (important pour
>>>> la reprise sur erreur)
>>>> Ton début de traitement d'un message revient à faire
>>>> var msg = queue.take(); // au pire tu as deux ou trois threads qui
>>>> prennent le même message
>>>> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync,
>>>> mais tu n'es pas obligé de faire pareil
>>>> {
>>>> if(!msg.Processing && !msg.Processed)
>>>> {
>>>> msg.Processing = true;
>>>> msg.StartProcessingDate = DateTime.Now; // à voir si la précision
>>>> est suffisante
>>>> processingQueue.Put(msg);
>>>> process (msg);
>>>> }
>>>> }
>>>> Quand ta queue de départ est vide, tu vas chercher dans la
>>>> processingQueue les message qui ne sont pas déjà traités et dont la date de
>>>> début est antérieure à x minutes, histoire de pallier aux défaillance de
>>>> tes consumers.
>>>> Avantage : tu lockes au niveau du message seulement, donc dans le pire
>>>> des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en
>>>> même temps du même message)
>>>> Mick, distribué.
>>>> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
>>>>> Salut Mathias,
>>>>> Idée : decorreler dédoublonnage et traitement.
>>>>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>>>>> dans une autre Q pour les consumers.
>>>>> Make sense ?
>>>>> Mathieu
>>>>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
>>>>> > Hello,
>>>>> > J'ai une petite question concernant la gestion de la concurrence.
>>>>> > J'ai un programme avec des threads "producer" et "consumer".
>>>>> > Je me sert d'une "blockingqueue" pour comminiquer.
>>>>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour
>>>>> que le consumer dépile la queue assez vite.
>>>>> > mais maintenant que je suis en prod, je suis sur un serveur mega
>>>>> puissant (avec beaucoup de core).
>>>>> > même si j'ai un seul thread producer, et le reste des cores pour le
>>>>> consumer, ils n'arrivent pas à dépiler.
>>>>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il
>>>>> n'a pas traité un message (doublons).
>>>>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock
>>>>> sur un HashSet.
>>>>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit
>>>>> "true" c'est qu'il
>>>>> > n'existait pas déjà, false dans le cas contraire.
>>>>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas
>>>>> déjà dans la hashset...
>>>>> > avez vous des idées pour eviter ce lock?
>>>>> > une idée que j'ai c'est de "router" les messages dans des blocking
>>>>> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un
>>>>> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà
>>>>> traité...
>>>>> > je garantie ansi que les doublons vont dans la même queue, que
>>>>> l'unicité est donc garantie, mais je répartie les locks par thread...
Je pensais aussi au filtre de Bloom, mais le pb c'est qu'il peut te donner
des faux positifs. C'a-d te dire qu'il y a un doublon, alors que c'est
faux. Du coup tu perds des messages (et au contraire, pas de traitement en
double).
Autre idée : N dédoublonneurs en parallèle. Tu envoies ton message M dans
la file p=Hash(M) mod N. Et tu es sûr que tous les doublons de M seront
dans la même file. Du coup, plus de lock.
Me gourre-je ?
Le 10 août 2012 10:16, Mick Philippon <mphilip...@octo.com> a écrit :
> Question :: Est-ce une catastrophe si un message est traité en double de
> temps à autre où pas ?
> Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne
> locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock).
> Certains messages seront traités en double, mais bon, c'est pas la mort.
> Si en revanche traiter deux fois le même message relève de la catastrophe
> nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide
> que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom,
> mais en gros c'est un champ de bits dans lequel on insère des infos. C'est
> destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet
> de tester l'existence avec un simple ou binaire. J'essaie de te retrouver
> le nom du truc, sauf si quelqu'un le connait de tête.
> * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta
> complexité tend vers O(1)
> Mick
> Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com> a écrit :
> Arf, non, car la traitement A génère le messages à partir d'un fichier
>> texte, et génère l'ID aussi.
>> Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
>> L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement
>> A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour
>> l'instant car ça bloque au dédoublonnage)
>>> Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le
>>> traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut
>>> pas être lui qui a la hashmap et qui dedoublonne ?
>>> Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a
>>> écrit :
>>> Tout d'abord, j'ai plusieurs queues.
>>> Donc je décolère déjà traitement et "détection des doublons".
>>> Avant je n'avais pas de problème car les traitements prenaient pas mal
>>> de CPU.
>>> Mais quand je suis passé en prod avec un serveur de ouf, les traitements
>>> vont trop vite et tout bloque sur la détection des doublons.
>>> message => queue 1 => traitements A => queue 2 => détection des
>>> doublons => queue 3 => traitements B
>>> le problème c'est que le "traitements A" va tellement vite, que la queue
>>> 2 est pleine (capé sinon ça explose en mémoire).
>>> et la queue3 est vide, car la "détection des doublons" va pas assez vite
>>> pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
>>> Concernant le lock au niveau du message: mes threads ne peuvent pas
>>> dépiler le même message (blockingQueue).
>>> J'ai des doublons au niveau de la source des messages.
>>> désolé de ne pas avoir été assez détaillé dans mon premier message,
>>> j'étais sur iPhone dans le metro :p
>>> PS: je sais que ma solution de "blocking queue" n'est pas idéal: je
>>> pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
>>> Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
>>>> Ou alors tu fais porter l'info de traitement au message. En gros tu
>>>> ajoutes à ton message deux booleens (volatiles) et une date : en cours de
>>>> traitement, traité, date de début de traitement (et un object de synchro)
>>>> Tu as une queue avec tous les messages qui ne sont pas en cours de
>>>> traitement et une avec les messages en cours de traitement (important pour
>>>> la reprise sur erreur)
>>>> Ton début de traitement d'un message revient à faire
>>>> var msg = queue.take(); // au pire tu as deux ou trois threads qui
>>>> prennent le même message
>>>> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync,
>>>> mais tu n'es pas obligé de faire pareil
>>>> {
>>>> if(!msg.Processing && !msg.Processed)
>>>> {
>>>> msg.Processing = true;
>>>> msg.StartProcessingDate = DateTime.Now; // à voir si la précision
>>>> est suffisante
>>>> processingQueue.Put(msg);
>>>> process (msg);
>>>> }
>>>> }
>>>> Quand ta queue de départ est vide, tu vas chercher dans la
>>>> processingQueue les message qui ne sont pas déjà traités et dont la date de
>>>> début est antérieure à x minutes, histoire de pallier aux défaillance de
>>>> tes consumers.
>>>> Avantage : tu lockes au niveau du message seulement, donc dans le pire
>>>> des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en
>>>> même temps du même message)
>>>> Mick, distribué.
>>>> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
>>>>> Salut Mathias,
>>>>> Idée : decorreler dédoublonnage et traitement.
>>>>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>>>>> dans une autre Q pour les consumers.
>>>>> Make sense ?
>>>>> Mathieu
>>>>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
>>>>> > Hello,
>>>>> > J'ai une petite question concernant la gestion de la concurrence.
>>>>> > J'ai un programme avec des threads "producer" et "consumer".
>>>>> > Je me sert d'une "blockingqueue" pour comminiquer.
>>>>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour
>>>>> que le consumer dépile la queue assez vite.
>>>>> > mais maintenant que je suis en prod, je suis sur un serveur mega
>>>>> puissant (avec beaucoup de core).
>>>>> > même si j'ai un seul thread producer, et le reste des cores pour le
>>>>> consumer, ils n'arrivent pas à dépiler.
>>>>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il
>>>>> n'a pas traité un message (doublons).
>>>>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock
>>>>> sur un HashSet.
>>>>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit
>>>>> "true" c'est qu'il
>>>>> > n'existait pas déjà, false dans le cas contraire.
>>>>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas
>>>>> déjà dans la hashset...
>>>>> > avez vous des idées pour eviter ce lock?
>>>>> > une idée que j'ai c'est de "router" les messages dans des blocking
>>>>> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un
>>>>> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà
>>>>> traité...
>>>>> > je garantie ansi que les doublons vont dans la même queue, que
>>>>> l'unicité est donc garantie, mais je répartie les locks par thread...
bonne idée pour le readWriteLock, je l'utilise déjà (mais j'ai beaucoup de doublons donc beaucoup de lock finalement)
ce n'est pas une catastrophe si j'ai des doublons, c'étais pour éviter de bourriner la base de donnée en sortie (mongo pour les curieux)
par contre, c'est catastrophique d'en oublier, donc je ne vois pas comment utiliser le filtre bloom dans ce cas.
par contre, je n'ai pas besoin parcourir le Hashset, ca peut donc etre une solution "destructrice"
concernant le Hash(m) mod N, c'est exactement l'idée de mon tout premier message, et c'est ce que j'ai fait.
inconvénient: le "getHashcode" n'est pas forcement la meilleur solution car il peut mal repartir les messages: je peux avoir 1 queue de 1000messages avec un thread qui carbure, et une queue vide avec un thread qui attend...
mais dans mon cas ça à l'air de globalement bien repartir
thx en tout cas pour le bloomfilter, on y pense rarement ;)
autour de ce sujet: qui utilise vraiment TPL Dataflow ? (en dehors des hello world?)--
Mathias Kluba
Twitter: @mathiaskluba
Blog: http://grozeille.com
Le vendredi 10 août 2012 à 10:57, Mathieu Despriee a écrit :
> Je pensais aussi au filtre de Bloom, mais le pb c'est qu'il peut te donner des faux positifs. C'a-d te dire qu'il y a un doublon, alors que c'est faux. Du coup tu perds des messages (et au contraire, pas de traitement en double).
> Autre idée : N dédoublonneurs en parallèle. Tu envoies ton message M dans la file p=Hash(M) mod N. Et tu es sûr que tous les doublons de M seront dans la même file. Du coup, plus de lock. > Me gourre-je ?
> Le 10 août 2012 10:16, Mick Philippon <mphilip...@octo.com (mailto:mphilip...@octo.com)> a écrit :
> > Question :: Est-ce une catastrophe si un message est traité en double de temps à autre où pas ?
> > Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock). Certains messages seront traités en double, mais bon, c'est pas la mort.
> > Si en revanche traiter deux fois le même message relève de la catastrophe nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom, mais en gros c'est un champ de bits dans lequel on insère des infos. C'est destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet de tester l'existence avec un simple ou binaire. J'essaie de te retrouver le nom du truc, sauf si quelqu'un le connait de tête.
> > * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta complexité tend vers O(1)
> > Mick
> > Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com (mailto:mathias.kl...@gmail.com)> a écrit :
> > > Arf, non, car la traitement A génère le messages à partir d'un fichier texte, et génère l'ID aussi.
> > > Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
> > > L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour l'instant car ça bloque au dédoublonnage)
> > > 2012/8/9 Mathieu Despriee <mdespr...@octo.com (mailto:mdespr...@octo.com)>
> > > > Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut pas être lui qui a la hashmap et qui dedoublonne ?
> > > > Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com (mailto:mathias.kl...@gmail.com)> a écrit :
> > > > > Tout d'abord, j'ai plusieurs queues.
> > > > > Donc je décolère déjà traitement et "détection des doublons".
> > > > > Avant je n'avais pas de problème car les traitements prenaient pas mal de CPU.
> > > > > Mais quand je suis passé en prod avec un serveur de ouf, les traitements vont trop vite et tout bloque sur la détection des doublons.
> > > > > message => queue 1 => traitements A => queue 2 => détection des doublons => queue 3 => traitements B
> > > > > le problème c'est que le "traitements A" va tellement vite, que la queue 2 est pleine (capé sinon ça explose en mémoire).
> > > > > et la queue3 est vide, car la "détection des doublons" va pas assez vite pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
> > > > > Concernant le lock au niveau du message: mes threads ne peuvent pas dépiler le même message (blockingQueue).
> > > > > J'ai des doublons au niveau de la source des messages.
> > > > > désolé de ne pas avoir été assez détaillé dans mon premier message, j'étais sur iPhone dans le metro :p
> > > > > PS: je sais que ma solution de "blocking queue" n'est pas idéal: je pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
> > > > > Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
> > > > > > Ou alors tu fais porter l'info de traitement au message. En gros tu ajoutes à ton message deux booleens (volatiles) et une date : en cours de traitement, traité, date de début de traitement (et un object de synchro)
> > > > > > Tu as une queue avec tous les messages qui ne sont pas en cours de traitement et une avec les messages en cours de traitement (important pour la reprise sur erreur)
> > > > > > Ton début de traitement d'un message revient à faire
> > > > > > var msg = queue.take(); // au pire tu as deux ou trois threads qui prennent le même message
> > > > > > lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais tu n'es pas obligé de faire pareil
> > > > > > {
> > > > > > if(!msg.Processing && !msg.Processed)
> > > > > > {
> > > > > > msg.Processing = true;
> > > > > > msg.StartProcessingDate = DateTime.Now; // à voir si la précision est suffisante
> > > > > > processingQueue.Put(msg);
> > > > > > process (msg);
> > > > > > }
> > > > > > }
> > > > > > Quand ta queue de départ est vide, tu vas chercher dans la processingQueue les message qui ne sont pas déjà traités et dont la date de début est antérieure à x minutes, histoire de pallier aux défaillance de tes consumers.
> > > > > > Avantage : tu lockes au niveau du message seulement, donc dans le pire des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même temps du même message)
> > > > > > Mick, distribué.
> > > > > > Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
> > > > > > > Salut Mathias,
> > > > > > > Idée : decorreler dédoublonnage et traitement.
> > > > > > > Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
> > > > > > > dans une autre Q pour les consumers.
> > > > > > > Make sense ?
> > > > > > > Mathieu
> > > > > > > Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
> > > > > > > > Hello,
> > > > > > > > J'ai une petite question concernant la gestion de la concurrence.
> > > > > > > > J'ai un programme avec des threads "producer" et "consumer".
> > > > > > > > Je me sert d'une "blockingqueue" pour comminiquer.
> > > > > > > > au départ, j'arrivais à ajuster le nombre de producer/consumers pour que le consumer dépile la queue assez vite.
> > > > > > > > mais maintenant que je suis en prod, je suis sur un serveur mega puissant (avec beaucoup de core).
> > > > > > > > même si j'ai un seul thread producer, et le reste des cores pour le consumer, ils n'arrivent pas à dépiler.
> > > > > > > > le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a pas traité un message (doublons).
> > > > > > > > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur un HashSet.
> > > > > > > > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true" c'est qu'il
> > > > > > > > n'existait pas déjà, false dans le cas contraire.
> > > > > > > > du coup, tous les threads bloquent pour vérifier si l'item n'est pas déjà dans la hashset...
> > > > > > > > avez vous des idées pour eviter ce lock?
> > > > > > > > une idée que j'ai c'est de "router" les messages dans des blocking queue différentes (1 par thread consumer) le routing se fait à l'aide d'un hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà traité...
> > > > > > > > je garantie ansi que les doublons vont dans la même queue, que l'unicité est donc garantie, mais je répartie les locks par thread...
Oui mais le filtre de bloom va devoir être mis à jour, donc on se retrouve
avec k calculs de hash, le tout locké, ce qui est pas forcément plus
intéressant.
Sinon utiliser un concurrent dictionary, qui est lock-free et propose de
meilleures performances.
> Je pensais aussi au filtre de Bloom, mais le pb c'est qu'il peut te donner
> des faux positifs. C'a-d te dire qu'il y a un doublon, alors que c'est
> faux. Du coup tu perds des messages (et au contraire, pas de traitement en
> double).
> Autre idée : N dédoublonneurs en parallèle. Tu envoies ton message M dans
> la file p=Hash(M) mod N. Et tu es sûr que tous les doublons de M seront
> dans la même file. Du coup, plus de lock.
> Me gourre-je ?
> Le 10 août 2012 10:16, Mick Philippon <mphilip...@octo.com> a écrit :
>> Question :: Est-ce une catastrophe si un message est traité en double de
>> temps à autre où pas ?
>> Si ton objectif est simplement d'économiser du temps de calcul, tu peux
>> ne locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock).
>> Certains messages seront traités en double, mais bon, c'est pas la mort.
>> Si en revanche traiter deux fois le même message relève de la catastrophe
>> nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide
>> que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom,
>> mais en gros c'est un champ de bits dans lequel on insère des infos. C'est
>> destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet
>> de tester l'existence avec un simple ou binaire. J'essaie de te retrouver
>> le nom du truc, sauf si quelqu'un le connait de tête.
>> * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta
>> complexité tend vers O(1)
>> Mick
>> Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com> a écrit :
>> Arf, non, car la traitement A génère le messages à partir d'un fichier
>>> texte, et génère l'ID aussi.
>>> Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
>>> L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement
>>> A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour
>>> l'instant car ça bloque au dédoublonnage)
>>>> Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si
>>>> le traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut
>>>> pas être lui qui a la hashmap et qui dedoublonne ?
>>>> Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a
>>>> écrit :
>>>> Tout d'abord, j'ai plusieurs queues.
>>>> Donc je décolère déjà traitement et "détection des doublons".
>>>> Avant je n'avais pas de problème car les traitements prenaient pas mal
>>>> de CPU.
>>>> Mais quand je suis passé en prod avec un serveur de ouf, les
>>>> traitements vont trop vite et tout bloque sur la détection des doublons.
>>>> message => queue 1 => traitements A => queue 2 => détection des
>>>> doublons => queue 3 => traitements B
>>>> le problème c'est que le "traitements A" va tellement vite, que la
>>>> queue 2 est pleine (capé sinon ça explose en mémoire).
>>>> et la queue3 est vide, car la "détection des doublons" va pas assez
>>>> vite pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
>>>> Concernant le lock au niveau du message: mes threads ne peuvent pas
>>>> dépiler le même message (blockingQueue).
>>>> J'ai des doublons au niveau de la source des messages.
>>>> désolé de ne pas avoir été assez détaillé dans mon premier message,
>>>> j'étais sur iPhone dans le metro :p
>>>> PS: je sais que ma solution de "blocking queue" n'est pas idéal: je
>>>> pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
>>>> Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
>>>>> Ou alors tu fais porter l'info de traitement au message. En gros tu
>>>>> ajoutes à ton message deux booleens (volatiles) et une date : en cours de
>>>>> traitement, traité, date de début de traitement (et un object de synchro)
>>>>> Tu as une queue avec tous les messages qui ne sont pas en cours de
>>>>> traitement et une avec les messages en cours de traitement (important pour
>>>>> la reprise sur erreur)
>>>>> Ton début de traitement d'un message revient à faire
>>>>> var msg = queue.take(); // au pire tu as deux ou trois threads qui
>>>>> prennent le même message
>>>>> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync,
>>>>> mais tu n'es pas obligé de faire pareil
>>>>> {
>>>>> if(!msg.Processing && !msg.Processed)
>>>>> {
>>>>> msg.Processing = true;
>>>>> msg.StartProcessingDate = DateTime.Now; // à voir si la précision
>>>>> est suffisante
>>>>> processingQueue.Put(msg);
>>>>> process (msg);
>>>>> }
>>>>> }
>>>>> Quand ta queue de départ est vide, tu vas chercher dans la
>>>>> processingQueue les message qui ne sont pas déjà traités et dont la date de
>>>>> début est antérieure à x minutes, histoire de pallier aux défaillance de
>>>>> tes consumers.
>>>>> Avantage : tu lockes au niveau du message seulement, donc dans le pire
>>>>> des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en
>>>>> même temps du même message)
>>>>> Mick, distribué.
>>>>> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
>>>>>> Salut Mathias,
>>>>>> Idée : decorreler dédoublonnage et traitement.
>>>>>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>>>>>> dans une autre Q pour les consumers.
>>>>>> Make sense ?
>>>>>> Mathieu
>>>>>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
>>>>>> > Hello,
>>>>>> > J'ai une petite question concernant la gestion de la concurrence.
>>>>>> > J'ai un programme avec des threads "producer" et "consumer".
>>>>>> > Je me sert d'une "blockingqueue" pour comminiquer.
>>>>>> > au départ, j'arrivais à ajuster le nombre de producer/consumers
>>>>>> pour que le consumer dépile la queue assez vite.
>>>>>> > mais maintenant que je suis en prod, je suis sur un serveur mega
>>>>>> puissant (avec beaucoup de core).
>>>>>> > même si j'ai un seul thread producer, et le reste des cores pour le
>>>>>> consumer, ils n'arrivent pas à dépiler.
>>>>>> > le problème vient du fait qu'a un moment, le consumer doit voir
>>>>>> s'il n'a pas traité un message (doublons).
>>>>>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock
>>>>>> sur un HashSet.
>>>>>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit
>>>>>> "true" c'est qu'il
>>>>>> > n'existait pas déjà, false dans le cas contraire.
>>>>>> > du coup, tous les threads bloquent pour vérifier si l'item n'est
>>>>>> pas déjà dans la hashset...
>>>>>> > avez vous des idées pour eviter ce lock?
>>>>>> > une idée que j'ai c'est de "router" les messages dans des blocking
>>>>>> queue différentes (1 par thread consumer) le routing se fait à l'aide d'un
>>>>>> hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà
>>>>>> traité...
>>>>>> > je garantie ansi que les doublons vont dans la même queue, que
>>>>>> l'unicité est donc garantie, mais je répartie les locks par thread...
J'avais même développé mon ConcurrentDico car je ne peux pas faire en .Net 4.0 (j'utilisais alors le ReadWriteLock).
Finalement, j'ai trouvé une version de la TPL pour .Net < 4.0.
Je ne vois d'ailleurs pas l'intérêt à forcer les gens à faire du .Net 4 pour ça… la TPL existait avant…
Mais si je fait:
if(!dico.TryGetValue(key, out fakeObject))
{
dico.Add(key, null);
}
J'ai pas ce que je veux: entre le moment ou je test si l'élément est présent, et le moment ou je l'ajoute, un autre thread peux être plus rapide…
Je peux aussi ajouter un catch quand ça arrive…
Le HashSet me plait dans le sens ou il une méthode "Add" qui renvoie "true/false" si l'élément existe déjà. Je lock que cette instruction (même si je lock aussi tout ce que fait la méthode Add..)
Est-ce que je me trompe? est-ce qu'il y a une autre façon d'utiliser le concurrentDico?
> Oui mais le filtre de bloom va devoir être mis à jour, donc on se retrouve avec k calculs de hash, le tout locké, ce qui est pas forcément plus intéressant.
> Sinon utiliser un concurrent dictionary, qui est lock-free et propose de meilleures performances.
> 2012/8/10 Mathieu Despriee <mdespr...@octo.com (mailto:mdespr...@octo.com)>
> > Je pensais aussi au filtre de Bloom, mais le pb c'est qu'il peut te donner des faux positifs. C'a-d te dire qu'il y a un doublon, alors que c'est faux. Du coup tu perds des messages (et au contraire, pas de traitement en double).
> > Autre idée : N dédoublonneurs en parallèle. Tu envoies ton message M dans la file p=Hash(M) mod N. Et tu es sûr que tous les doublons de M seront dans la même file. Du coup, plus de lock. > > Me gourre-je ?
> > Le 10 août 2012 10:16, Mick Philippon <mphilip...@octo.com (mailto:mphilip...@octo.com)> a écrit :
> > > Question :: Est-ce une catastrophe si un message est traité en double de temps à autre où pas ?
> > > Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock). Certains messages seront traités en double, mais bon, c'est pas la mort.
> > > Si en revanche traiter deux fois le même message relève de la catastrophe nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom, mais en gros c'est un champ de bits dans lequel on insère des infos. C'est destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet de tester l'existence avec un simple ou binaire. J'essaie de te retrouver le nom du truc, sauf si quelqu'un le connait de tête.
> > > * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta complexité tend vers O(1)
> > > Mick
> > > Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com (mailto:mathias.kl...@gmail.com)> a écrit :
> > > > Arf, non, car la traitement A génère le messages à partir d'un fichier texte, et génère l'ID aussi.
> > > > Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
> > > > L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour l'instant car ça bloque au dédoublonnage)
> > > > 2012/8/9 Mathieu Despriee <mdespr...@octo.com (mailto:mdespr...@octo.com)>
> > > > > Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut pas être lui qui a la hashmap et qui dedoublonne ?
> > > > > Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com (mailto:mathias.kl...@gmail.com)> a écrit :
> > > > > > Tout d'abord, j'ai plusieurs queues.
> > > > > > Donc je décolère déjà traitement et "détection des doublons".
> > > > > > Avant je n'avais pas de problème car les traitements prenaient pas mal de CPU.
> > > > > > Mais quand je suis passé en prod avec un serveur de ouf, les traitements vont trop vite et tout bloque sur la détection des doublons.
> > > > > > message => queue 1 => traitements A => queue 2 => détection des doublons => queue 3 => traitements B
> > > > > > le problème c'est que le "traitements A" va tellement vite, que la queue 2 est pleine (capé sinon ça explose en mémoire).
> > > > > > et la queue3 est vide, car la "détection des doublons" va pas assez vite pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
> > > > > > Concernant le lock au niveau du message: mes threads ne peuvent pas dépiler le même message (blockingQueue).
> > > > > > J'ai des doublons au niveau de la source des messages.
> > > > > > désolé de ne pas avoir été assez détaillé dans mon premier message, j'étais sur iPhone dans le metro :p
> > > > > > PS: je sais que ma solution de "blocking queue" n'est pas idéal: je pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
> > > > > > Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
> > > > > > > Ou alors tu fais porter l'info de traitement au message. En gros tu ajoutes à ton message deux booleens (volatiles) et une date : en cours de traitement, traité, date de début de traitement (et un object de synchro)
> > > > > > > Tu as une queue avec tous les messages qui ne sont pas en cours de traitement et une avec les messages en cours de traitement (important pour la reprise sur erreur)
> > > > > > > Ton début de traitement d'un message revient à faire
> > > > > > > var msg = queue.take(); // au pire tu as deux ou trois threads qui prennent le même message
> > > > > > > lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais tu n'es pas obligé de faire pareil
> > > > > > > {
> > > > > > > if(!msg.Processing && !msg.Processed)
> > > > > > > {
> > > > > > > msg.Processing = true;
> > > > > > > msg.StartProcessingDate = DateTime.Now; // à voir si la précision est suffisante
> > > > > > > processingQueue.Put(msg);
> > > > > > > process (msg);
> > > > > > > }
> > > > > > > }
> > > > > > > Quand ta queue de départ est vide, tu vas chercher dans la processingQueue les message qui ne sont pas déjà traités et dont la date de début est antérieure à x minutes, histoire de pallier aux défaillance de tes consumers.
> > > > > > > Avantage : tu lockes au niveau du message seulement, donc dans le pire des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même temps du même message)
> > > > > > > Mick, distribué.
> > > > > > > Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
> > > > > > > > Salut Mathias,
> > > > > > > > Idée : decorreler dédoublonnage et traitement.
> > > > > > > > Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
> > > > > > > > dans une autre Q pour les consumers.
> > > > > > > > Make sense ?
> > > > > > > > Mathieu
> > > > > > > > Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
> > > > > > > > > Hello,
> > > > > > > > > J'ai une petite question concernant la gestion de la concurrence.
> > > > > > > > > J'ai un programme avec des threads "producer" et "consumer".
> > > > > > > > > Je me sert d'une "blockingqueue" pour comminiquer.
> > > > > > > > > au départ, j'arrivais à ajuster le nombre de producer/consumers pour que le consumer dépile la queue assez vite.
> > > > > > > > > mais maintenant que je suis en prod, je suis sur un serveur mega puissant (avec beaucoup de core).
> > > > > > > > > même si j'ai un seul thread producer, et le reste des cores pour le consumer, ils n'arrivent pas à dépiler.
> > > > > > > > > le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a pas traité un message (doublons).
> > > > > > > > > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur un HashSet.
> > > > > > > > > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true" c'est qu'il
> > > > > > > > > n'existait pas déjà, false dans le cas contraire.
> > > > > > > > > du coup, tous les threads bloquent pour vérifier si l'item n'est pas déjà dans la hashset...
> > > > > > > > > avez vous des idées pour eviter ce lock?
> > > > > > > > > une idée que j'ai c'est de "router" les messages dans des blocking queue différentes (1 par thread consumer) le routing se fait à l'aide d'un hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà traité...
> > > > > > > > > je garantie ansi que les doublons vont dans la même queue, que l'unicité est donc garantie, mais je répartie les locks par thread...
> J'avais même développé mon ConcurrentDico car je ne peux pas faire en .Net 4.0 (j'utilisais alors le ReadWriteLock).
> Finalement, j'ai trouvé une version de la TPL pour .Net < 4.0.
> Je ne vois d'ailleurs pas l'intérêt à forcer les gens à faire du .Net 4 pour ça… la TPL existait avant…
> J'ai pas ce que je veux: entre le moment ou je test si l'élément est présent, et le moment ou je l'ajoute, un autre thread peux être plus rapide…
> Je peux aussi ajouter un catch quand ça arrive…
> Le HashSet me plait dans le sens ou il une méthode "Add" qui renvoie "true/false" si l'élément existe déjà. Je lock que cette instruction (même si je lock aussi tout ce que fait la méthode Add..)
> Est-ce que je me trompe? est-ce qu'il y a une autre façon d'utiliser le concurrentDico?
> -- > Mathias Kluba
> Twitter: @mathiaskluba
> Blog: http://grozeille.com > Le vendredi 10 août 2012 à 21:36, Yann Schwartz a écrit :
>> Oui mais le filtre de bloom va devoir être mis à jour, donc on se retrouve avec k calculs de hash, le tout locké, ce qui est pas forcément plus intéressant.
>> Sinon utiliser un concurrent dictionary, qui est lock-free et propose de meilleures performances.
>> 2012/8/10 Mathieu Despriee <mdespr...@octo.com>
>>> Je pensais aussi au filtre de Bloom, mais le pb c'est qu'il peut te donner des faux positifs. C'a-d te dire qu'il y a un doublon, alors que c'est faux. Du coup tu perds des messages (et au contraire, pas de traitement en double).
>>> Autre idée : N dédoublonneurs en parallèle. Tu envoies ton message M dans la file p=Hash(M) mod N. Et tu es sûr que tous les doublons de M seront dans la même file. Du coup, plus de lock. >>> Me gourre-je ?
>>> Le 10 août 2012 10:16, Mick Philippon <mphilip...@octo.com> a écrit :
>>>> Question :: Est-ce une catastrophe si un message est traité en double de temps à autre où pas ?
>>>> Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock). Certains messages seront traités en double, mais bon, c'est pas la mort.
>>>> Si en revanche traiter deux fois le même message relève de la catastrophe nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom, mais en gros c'est un champ de bits dans lequel on insère des infos. C'est destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet de tester l'existence avec un simple ou binaire. J'essaie de te retrouver le nom du truc, sauf si quelqu'un le connait de tête.
>>>> * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta complexité tend vers O(1)
>>>> Mick
>>>> Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com> a écrit :
>>>>> Arf, non, car la traitement A génère le messages à partir d'un fichier texte, et génère l'ID aussi.
>>>>> Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
>>>>> L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour l'instant car ça bloque au dédoublonnage)
>>>>> 2012/8/9 Mathieu Despriee <mdespr...@octo.com>
>>>>>> Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut pas être lui qui a la hashmap et qui dedoublonne ?
>>>>>> Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com> a écrit :
>>>>>>> Tout d'abord, j'ai plusieurs queues.
>>>>>>> Donc je décolère déjà traitement et "détection des doublons".
>>>>>>> Avant je n'avais pas de problème car les traitements prenaient pas mal de CPU.
>>>>>>> Mais quand je suis passé en prod avec un serveur de ouf, les traitements vont trop vite et tout bloque sur la détection des doublons.
>>>>>>> message => queue 1 => traitements A => queue 2 => détection des doublons => queue 3 => traitements B
>>>>>>> le problème c'est que le "traitements A" va tellement vite, que la queue 2 est pleine (capé sinon ça explose en mémoire).
>>>>>>> et la queue3 est vide, car la "détection des doublons" va pas assez vite pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
>>>>>>> Concernant le lock au niveau du message: mes threads ne peuvent pas dépiler le même message (blockingQueue).
>>>>>>> J'ai des doublons au niveau de la source des messages.
>>>>>>> désolé de ne pas avoir été assez détaillé dans mon premier message, j'étais sur iPhone dans le metro :p
>>>>>>> PS: je sais que ma solution de "blocking queue" n'est pas idéal: je pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
>>>>>>> Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
>>>>>>>> Ou alors tu fais porter l'info de traitement au message. En gros tu ajoutes à ton message deux booleens (volatiles) et une date : en cours de traitement, traité, date de début de traitement (et un object de synchro)
>>>>>>>> Tu as une queue avec tous les messages qui ne sont pas en cours de traitement et une avec les messages en cours de traitement (important pour la reprise sur erreur)
>>>>>>>> Ton début de traitement d'un message revient à faire
>>>>>>>> var msg = queue.take(); // au pire tu as deux ou trois threads qui prennent le même message
>>>>>>>> lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais tu n'es pas obligé de faire pareil
>>>>>>>> {
>>>>>>>> if(!msg.Processing && !msg.Processed)
>>>>>>>> {
>>>>>>>> msg.Processing = true;
>>>>>>>> msg.StartProcessingDate = DateTime.Now; // à voir si la précision est suffisante
>>>>>>>> processingQueue.Put(msg);
>>>>>>>> process (msg);
>>>>>>>> }
>>>>>>>> }
>>>>>>>> Quand ta queue de départ est vide, tu vas chercher dans la processingQueue les message qui ne sont pas déjà traités et dont la date de début est antérieure à x minutes, histoire de pallier aux défaillance de tes consumers.
>>>>>>>> Avantage : tu lockes au niveau du message seulement, donc dans le pire des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même temps du même message)
>>>>>>>> Mick, distribué.
>>>>>>>> Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com> a écrit :
>>>>>>>>> Salut Mathias,
>>>>>>>>> Idée : decorreler dédoublonnage et traitement.
>>>>>>>>> Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
>>>>>>>>> dans une autre Q pour les consumers.
>>>>>>>>> Make sense ?
>>>>>>>>> Mathieu
>>>>>>>>> Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com> a écrit :
>>>>>>>>> > Hello,
>>>>>>>>> > J'ai une petite question concernant la gestion de la concurrence.
>>>>>>>>> > J'ai un programme avec des threads "producer" et "consumer".
>>>>>>>>> > Je me sert d'une "blockingqueue" pour comminiquer.
>>>>>>>>> > au départ, j'arrivais à ajuster le nombre de producer/consumers pour que le consumer dépile la queue assez vite.
>>>>>>>>> > mais maintenant que je suis en prod, je suis sur un serveur mega puissant (avec beaucoup de core).
>>>>>>>>> > même si j'ai un seul thread producer, et le reste des cores pour le consumer, ils n'arrivent pas à dépiler.
>>>>>>>>> > le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a pas traité un message (doublons).
>>>>>>>>> > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur un HashSet.
>>>>>>>>> > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true" c'est qu'il
>>>>>>>>> > n'existait pas déjà, false dans le cas contraire.
>>>>>>>>> > du coup, tous les threads bloquent pour vérifier si l'item n'est pas déjà dans la hashset...
>>>>>>>>> > avez vous des idées pour eviter ce lock?
>>>>>>>>> > une idée que j'ai c'est de "router" les messages dans des blocking queue différentes (1 par thread consumer) le routing se fait à l'aide d'un hash sur l'id du message. et chaque thread possède sa Hashset d'id déjà traité...
>>>>>>>>> > je garantie ansi que les doublons vont dans la même queue, que l'unicité est donc garantie, mais je répartie les locks par thread...
Thx, je suis passé à coté :) c'est exactement ce qu'il me faut.
Ceci dit, j'ai déjà opté pour la solution d'une queue par thread avec une répartition avec HashCode modulo NbQueue, mais c'est bon à savoir.
Je vais de ce pas regardé comment ils ont implémenté ça ;)
> Ça dépend de ce que tu veux faire concrètement, mais t'as TryAdd et GetOrAdd sur le ConcurrentDico.
> On Aug 11, 2012, at 6:30 PM, Mathias Kluba wrote:
> > Hello,
> > J'avais même développé mon ConcurrentDico car je ne peux pas faire en .Net 4.0 (j'utilisais alors le ReadWriteLock).
> > Finalement, j'ai trouvé une version de la TPL pour .Net < 4.0.
> > Je ne vois d'ailleurs pas l'intérêt à forcer les gens à faire du .Net 4 pour ça… la TPL existait avant…
> > J'ai pas ce que je veux: entre le moment ou je test si l'élément est présent, et le moment ou je l'ajoute, un autre thread peux être plus rapide…
> > Je peux aussi ajouter un catch quand ça arrive…
> > Le HashSet me plait dans le sens ou il une méthode "Add" qui renvoie "true/false" si l'élément existe déjà. Je lock que cette instruction (même si je lock aussi tout ce que fait la méthode Add..)
> > Est-ce que je me trompe? est-ce qu'il y a une autre façon d'utiliser le concurrentDico?
> > --
> > Mathias Kluba
> > Twitter: @mathiaskluba
> > Blog: http://grozeille.com > > Le vendredi 10 août 2012 à 21:36, Yann Schwartz a écrit :
> > > Oui mais le filtre de bloom va devoir être mis à jour, donc on se retrouve avec k calculs de hash, le tout locké, ce qui est pas forcément plus intéressant.
> > > Sinon utiliser un concurrent dictionary, qui est lock-free et propose de meilleures performances.
> > > 2012/8/10 Mathieu Despriee <mdespr...@octo.com (mailto:mdespr...@octo.com)>
> > > > Je pensais aussi au filtre de Bloom, mais le pb c'est qu'il peut te donner des faux positifs. C'a-d te dire qu'il y a un doublon, alors que c'est faux. Du coup tu perds des messages (et au contraire, pas de traitement en double).
> > > > Autre idée : N dédoublonneurs en parallèle. Tu envoies ton message M dans la file p=Hash(M) mod N. Et tu es sûr que tous les doublons de M seront dans la même file. Du coup, plus de lock.
> > > > Me gourre-je ?
> > > > Le 10 août 2012 10:16, Mick Philippon <mphilip...@octo.com (mailto:mphilip...@octo.com)> a écrit :
> > > > > Question :: Est-ce une catastrophe si un message est traité en double de temps à autre où pas ?
> > > > > Si ton objectif est simplement d'économiser du temps de calcul, tu peux ne locker qu'en écriture (ou au besoin utiliser un ReaderWriterSlimLock). Certains messages seront traités en double, mais bon, c'est pas la mort.
> > > > > Si en revanche traiter deux fois le même message relève de la catastrophe nucléaire, il va falloir ruser un peu plus. Il existe un truc plus rapide que le hashset pour déterminer les doublons*. Je ne retrouve plus le nom, mais en gros c'est un champ de bits dans lequel on insère des infos. C'est destructeur (cad que tu ne peux retrouver un truc inséré)) mais ça permet de tester l'existence avec un simple ou binaire. J'essaie de te retrouver le nom du truc, sauf si quelqu'un le connait de tête.
> > > > > * du moins sur un hashset qui évolue, vu que sur un hashset fixe, ta complexité tend vers O(1)
> > > > > Mick
> > > > > Le 9 août 2012 22:45, mathias kluba <mathias.kl...@gmail.com (mailto:mathias.kl...@gmail.com)> a écrit :
> > > > > > Arf, non, car la traitement A génère le messages à partir d'un fichier texte, et génère l'ID aussi.
> > > > > > Même si je colle la Hashmap dans ce thread, ça ira pas plus vite.
> > > > > > L'intérêt c'est que je puisse avoir plusieurs threads pour le traitement A, pour traiter encore plus de fichiers (ce qui ne sert à rien pour l'instant car ça bloque au dédoublonnage)
> > > > > > 2012/8/9 Mathieu Despriee <mdespr...@octo.com (mailto:mdespr...@octo.com)>
> > > > > > > Peut-être qu'il y a un truc que je pige pas, mais : je suppose que si le traitement A est rapide, il n'est que sur une thread. Est-ce que ça peut pas être lui qui a la hashmap et qui dedoublonne ?
> > > > > > > Le 9 août 2012 à 22:14, mathias kluba <mathias.kl...@gmail.com (mailto:mathias.kl...@gmail.com)> a écrit :
> > > > > > > > Tout d'abord, j'ai plusieurs queues.
> > > > > > > > Donc je décolère déjà traitement et "détection des doublons".
> > > > > > > > Avant je n'avais pas de problème car les traitements prenaient pas mal de CPU.
> > > > > > > > Mais quand je suis passé en prod avec un serveur de ouf, les traitements vont trop vite et tout bloque sur la détection des doublons.
> > > > > > > > message => queue 1 => traitements A => queue 2 => détection des doublons => queue 3 => traitements B
> > > > > > > > le problème c'est que le "traitements A" va tellement vite, que la queue 2 est pleine (capé sinon ça explose en mémoire).
> > > > > > > > et la queue3 est vide, car la "détection des doublons" va pas assez vite pour dépiler la queue 2, et pas assez vite pour remplir la queue 3.
> > > > > > > > Concernant le lock au niveau du message: mes threads ne peuvent pas dépiler le même message (blockingQueue).
> > > > > > > > J'ai des doublons au niveau de la source des messages.
> > > > > > > > désolé de ne pas avoir été assez détaillé dans mon premier message, j'étais sur iPhone dans le metro :p
> > > > > > > > PS: je sais que ma solution de "blocking queue" n'est pas idéal: je pourrais déjà utiliser TPL Dataflow, voir même le Disruptor.
> > > > > > > > Le jeudi 9 août 2012 21:42:16 UTC+2, Mick Philippon a écrit :
> > > > > > > > > Ou alors tu fais porter l'info de traitement au message. En gros tu ajoutes à ton message deux booleens (volatiles) et une date : en cours de traitement, traité, date de début de traitement (et un object de synchro)
> > > > > > > > > Tu as une queue avec tous les messages qui ne sont pas en cours de traitement et une avec les messages en cours de traitement (important pour la reprise sur erreur)
> > > > > > > > > Ton début de traitement d'un message revient à faire
> > > > > > > > > var msg = queue.take(); // au pire tu as deux ou trois threads qui prennent le même message
> > > > > > > > > lock(msg.__sync) // J'appelle toujours mes objets de synchro __sync, mais tu n'es pas obligé de faire pareil
> > > > > > > > > {
> > > > > > > > > if(!msg.Processing && !msg.Processed)
> > > > > > > > > {
> > > > > > > > > msg.Processing = true;
> > > > > > > > > msg.StartProcessingDate = DateTime.Now; // à voir si la précision est suffisante
> > > > > > > > > processingQueue.Put(msg);
> > > > > > > > > process (msg);
> > > > > > > > > }
> > > > > > > > > }
> > > > > > > > > Quand ta queue de départ est vide, tu vas chercher dans la processingQueue les message qui ne sont pas déjà traités et dont la date de début est antérieure à x minutes, histoire de pallier aux défaillance de tes consumers.
> > > > > > > > > Avantage : tu lockes au niveau du message seulement, donc dans le pire des cas tu vas bloquer deux ou trois thread (ceux qui ont fait le take en même temps du même message)
> > > > > > > > > Mick, distribué.
> > > > > > > > > Le 9 août 2012 21:35, Mathieu Despriee <mdes...@octo.com (http://octo.com)> a écrit :
> > > > > > > > > > Salut Mathias,
> > > > > > > > > > Idée : decorreler dédoublonnage et traitement.
> > > > > > > > > > Une thread de dedoublonnage (qui fait que ça) et qui remet le résultat
> > > > > > > > > > dans une autre Q pour les consumers.
> > > > > > > > > > Make sense ?
> > > > > > > > > > Mathieu
> > > > > > > > > > Le 9 août 2012 à 20:46, Mathias Kluba <mathia...@gmail.com (http://gmail.com)> a écrit :
> > > > > > > > > > > Hello,
> > > > > > > > > > > J'ai une petite question concernant la gestion de la concurrence.
> > > > > > > > > > > J'ai un programme avec des threads "producer" et "consumer".
> > > > > > > > > > > Je me sert d'une "blockingqueue" pour comminiquer.
> > > > > > > > > > > au départ, j'arrivais à ajuster le nombre de producer/consumers pour que le consumer dépile la queue assez vite.
> > > > > > > > > > > mais maintenant que je suis en prod, je suis sur un serveur mega puissant (avec beaucoup de core).
> > > > > > > > > > > même si j'ai un seul thread producer, et le reste des cores pour le consumer, ils n'arrivent pas à dépiler.
> > > > > > > > > > > le problème vient du fait qu'a un moment, le consumer doit voir s'il n'a pas traité un message (doublons).
> > > > > > > > > > > pour ça, je fait un truc moche qui bloque tous les threads: un lock sur un HashSet.
> > > > > > > > > > > j'essaye d'ajouter l'id du message dans le HashSet, si ça renvoit "true" c'est qu'il
> > > > > > > > > > > n'existait pas déjà,