Part 2. Many cores · lesson 13 of 42
13. The leader¶
A group of cores gives one report and one result, and both come from its leader, the first core of the group. This lesson relies on lesson 11, A core group, lesson 6, A result and lesson 5, Commands.
Why a leader¶
When wait returns, it returns one event for one command, however many cores ran it. The
cores have to agree on who tells the dispatcher (lesson 5, Commands). The rule is simple: every
other core of the group reports to the first one, and the first one, once all have
reported, reports to the dispatcher. That core is the leader. This is why a command is
done only when every core of its group is.
The result word of a command (lesson 6, A result) travels with the leader's report. So the
result a Python program reads is whatever the leader's writer.cpp set, and nothing a
different core of the group set arrives.
group=(5, 4): three cores report to the leader, core 5, and the leader reports to the dispatcher with its own result.The general form¶
There is nothing new to write: the leader is the core with rank 0, and the code is that of lesson 6, A result:
handle = run.submit(KIND, group=(FIRST, COUNT), result=True)
event = run.wait(handle, timeout=SECONDS)
VALUEis a number of 32 bits. Every core of the group may callwriter.result; only the call of the coreFIRST, the one with rank 0, reaches Python asevent.result.- A leader that does not call
writer.resultgivesevent.result == 0. - The group's leader is always the core
FIRSTofgroup=(FIRST, COUNT).
One line at a time¶
writer.run([&](const km_command_t& command) {
const uint32_t rank = writer.me() - km::first(command);
if (km::handler(command) == kHandlerEveryone) {
writer.result(writer.me());
}
if (km::handler(command) == kHandlerOnlyOne && rank == command.a0) {
writer.result(writer.me());
}
return true;
});
writer.cpp works out its rank. For the handler Everyone, every core of the group sets
its own core number as the result. For the handler OnlyOne, only the core whose rank is
the first argument does.
handle = run.submit("everyone", group=(5, 4), result=True)
event = run.wait(handle, timeout=10.0)
print(f"cores 5 to 8 all answer; Python receives {event.result}")
The group is cores 5 to 8. All four set a result, 5, 6, 7 and 8, and Python receives 5: the leader's.
received = []
for rank in range(4):
handle = run.submit("only_one", group=(5, 4), args=[rank], result=True)
event = run.wait(handle, timeout=10.0)
received.append(event.result)
print(f"only rank {rank} answers; Python receives {event.result}")
Four more commands to the same group, in which one rank at a time sets its result. When the rank is 0 the result arrives, as 5. When it is 1, 2 or 3, the core sets its result, but it is not the leader's, and Python receives 0, as if nobody had answered.
The whole program¶
"""The leader: of a group of four cores, only the first core's result reaches Python."""
from pathlib import Path
import komorebi as km
HERE = Path(__file__).resolve().parent
program = km.Program(
"the_leader",
kernels=HERE / "kernels",
handlers=HERE / "kernels" / "handlers.h",
)
with km.Session() as session:
run = session.load(program)
handle = run.submit("everyone", group=(5, 4), result=True)
event = run.wait(handle, timeout=10.0)
print(f"cores 5 to 8 all answer; Python receives {event.result}")
received = []
for rank in range(4):
handle = run.submit("only_one", group=(5, 4), args=[rank], result=True)
event = run.wait(handle, timeout=10.0)
received.append(event.result)
print(f"only rank {rank} answers; Python receives {event.result}")
run.stop()
assert received == [5, 0, 0, 0]
// The writer kernel: sets a result word, on every core or on one core of the group.
#include "komorebi/device/km_worker.hpp"
#include "handlers.h"
void kernel_main() {
km::Writer writer;
writer.run([&](const km_command_t& command) {
const uint32_t rank = writer.me() - km::first(command);
if (km::handler(command) == kHandlerEveryone) {
writer.result(writer.me());
}
if (km::handler(command) == kHandlerOnlyOne && rank == command.a0) {
writer.result(writer.me());
}
return true;
});
}
cores 5 to 8 all answer; Python receives 5
only rank 0 answers; Python receives 5
only rank 1 answers; Python receives 0
only rank 2 answers; Python receives 0
only rank 3 answers; Python receives 0
The script checks the four numbers of the second part against [5, 0, 0, 0].
Common mistakes¶
- Expecting a result from another core. A result of 0 can mean that the leader set 0, or that nothing was set, or that a core other than the leader set it. The three look the same from Python.
- Putting the work that sets the result in the last core, "because it finishes last". The group's result is the first core's.
- Thinking the leader is slower or does extra work. It only waits for the reports of the others, and that wait is inside the command's time.
That is the group. What a program does with many cores that each hold a different piece of data comes in Part 3.
Next: lesson 14, Placing groups chooses where on the card a group goes.