Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Events in this case to act slowly #218

Open
ziaratban opened this issue Aug 31, 2021 · 5 comments
Open

Events in this case to act slowly #218

ziaratban opened this issue Aug 31, 2021 · 5 comments

Comments

@ziaratban
Copy link

Hi
image

<?php
use parallel\{Runtime,Channel};

$ch1 = Channel::Make('a',Channel::Infinite);
$r1 = new Runtime;
$r1->Run(static function()use($ch1){

    $r2 = new \parallel\Runtime;
    $ch2 = \parallel\Channel::Make('b',Channel::Infinite);
    $r2->Run(static function()use($ch2){
        while(true){
            $i = 100;
            while($i--)
                $ch2->Send(rand(1,999999999));
            sleep(1);
        }
    });

    $events = new \parallel\Events;
    $events->SetBlocking(false);
    $events->AddChannel($ch1);
    $events->AddChannel($ch2);
    while(true){
        $fetch = 0;
        while($event = $events->Poll()){ // <--------- Very slow
            $events->AddChannel($event->object);
            $fetch++;
        }
        echo "Fetch(".date('i:s').") > $fetch\n";
        sleep(1);
    }
});

while(true){
    $ch1->Send(1);
    usleep(500000);
}
@idanbenezra
Copy link

idanbenezra commented Jul 18, 2024

Hey all,

I'm experiencing the same behavior on the latest version 1.2.2.
It happens when opening lots of threads with $events->Poll().

<?php

use parallel\Channel;
use parallel\Events;
use parallel\Runtime;

$total_threads = $argv[1] ?? 0;
if(!$total_threads) die("Please provide number of threads\n");

$threads = [];

$channel = new Channel(100);

for($i = 0; $i < $total_threads; $i++) {
	$threads[] = (new Runtime())->run(static function(Channel $channel) {
		$events = new Events();
		$events->addChannel($channel);

		while(true) {
			while(($event = $events->poll())) {
				$events->addChannel($event->object);
			}
		}

	}, [$channel]);
}

$start = microtime(true);

for($j = 0; $j < 100000000; $j++) {
	
	if(!($j % 10000000)) {
		echo "10m iterations in " . (microtime(true) - $start) . " seconds\n";
		$start = microtime(true);
	} 
	
}

Then running from CLI (few examples):

With only 1 thread:

# php parallel-tester.php 1
10m iterations in 2.1457672119141E-6 seconds
10m iterations in 0.22435593605042 seconds
10m iterations in 0.23026609420776 seconds
10m iterations in 0.224534034729 seconds
10m iterations in 0.22518491744995 seconds
10m iterations in 0.22755908966064 seconds
10m iterations in 0.22358298301697 seconds
10m iterations in 0.24414205551147 seconds
10m iterations in 0.23095417022705 seconds
10m iterations in 0.22916793823242 seconds

With 10 threads (there isn't any change):

# php parallel-tester.php 10
10m iterations in 9.5367431640625E-7 seconds
10m iterations in 0.22031497955322 seconds
10m iterations in 0.22988986968994 seconds
10m iterations in 0.22356510162354 seconds
10m iterations in 0.22263288497925 seconds
10m iterations in 0.23073315620422 seconds
10m iterations in 0.22678804397583 seconds
10m iterations in 0.22507619857788 seconds
10m iterations in 0.22656798362732 seconds
10m iterations in 0.22526717185974 seconds

With 50 threads (very very slow):

# php parallel-tester.php 50
10m iterations in 5.0067901611328E-6 seconds
10m iterations in 1.1272690296173 seconds
10m iterations in 1.1491401195526 seconds
10m iterations in 1.0572350025177 seconds
10m iterations in 1.1068761348724 seconds
10m iterations in 1.009134054184 seconds
10m iterations in 1.1045799255371 seconds
10m iterations in 1.3764989376068 seconds
10m iterations in 1.3563649654388 seconds
10m iterations in 1.3363428115845 seconds

Is there something I'm doing wrong with the poll?

@elialum
Copy link

elialum commented Jul 19, 2024

I tried the same scenario above (provided by @idanbenezra )
I can confirm it's also happening on my end.

Maybe @realFlowControl / @krakjoe can advise?

@realFlowControl
Copy link
Collaborator

Hey @idanbenezra,

I checked your example and indeed, the call to \parallel\Events::poll() is CPU intensive. If you have a lot of threads that poll() on the same \parallel\Events instance you might additionally see that threads start waiting on mutexes which are needed for synchronisation. There might be better implementations but my time is limited, maybe I can come up with something, but this requires more time.

So in your case, with 50 threads you have 50 threads busy-waiting, which is most likely more than you have CPU cores, so your main thread get's scheduled less often and such takes longer to complete the 10 million iterations.

I don't know about the use case you have, so far I was mostly using poll() to "wait" on the main thread on the results of my worker threads.

@realFlowControl
Copy link
Collaborator

Hey @ziaratban,

you might see the same problem, but not as extreme, as this is just 3 threads and 2 channels. But as I mentioned in the earlier comment, \parallel\Events::poll() is a busy wait implementation. In your case though, by setting it non-blocking, it returns early if it did not find any readable channels.

@pielonet
Copy link

pielonet commented Oct 2, 2024

Hi @idanbenezra,

I tested your script and got the same results.

The fact is the duration you get do not surprise me much:

With 50 threads, your operating system (Linux I guess ?) scheduler is having a hard time switching from one thread to the other and this slows down you main thread too.

As a matter of fact I notice performance starts to degrade exactly at 11 threads and remains constant (does not degrade much) up to 50 threads.

My opinion is : this is (Linux-)scheduler-related.

To be more precise: I used command vmstat 1 to monitor the number of Context Switches per second ("cs"):

  • With 10 threads cs is rougly 300000
  • With 11 threads it drops to 150000
  • With 50 threads it is roughly 80000

Maybe this post gives an explanation : https://unix.stackexchange.com/questions/80424/why-using-more-threads-makes-it-slower-than-using-less-threads

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants