#!/usr/bin/env dub
/+ dub.sdl:
name "gcd_qos_parallelism"
platforms "osx"
targetPath "build"
+/
/**
* GCD — how wide is "wide"? `pthread_qos_max_parallelism` per QoS class.
*
* `dispatch_apply` does not fan out to `nproc` threads. libdispatch sizes it
* with `_dispatch_qos_max_parallelism(qos, DISPATCH_MAX_PARALLELISM_ACTIVE)`
* (`src/shims.h`), which calls `pthread_qos_max_parallelism(qos_class, flags)`
* and falls back to the CPU count only if that returns zero. On Apple silicon
* the answer is *not* uniform: the background QoS class is confined to the
* efficiency cluster, so its parallelism is the E-core count, not the total.
*
* This program prints the kernel's answer for each QoS class in both the
* logical and the `PTHREAD_MAX_PARALLELISM_PHYSICAL` flavour, and asserts the
* ordering libdispatch relies on (background never wider than default).
*
* Companion to the GCD deep-dive:
* see docs/research/async-io/gcd/index.md § "`dispatch_apply` and QoS-aware parallelism".
*
* Run with: `dub run --single qos-parallelism.d`
*
* Portability: macOS only. `pthread_qos_max_parallelism` exists since macOS
* 10.13; on an older host it returns a negative value and the program prints a
* `SKIP:` line and exits 0.
*/
module (module) gcd_qos_parallelismGCD — how wide is "wide"? pthread_qos_max_parallelism per QoS class.
dispatch_apply does not fan out to nproc threads. libdispatch sizes it
with _dispatch_qos_max_parallelism(qos, DISPATCH_MAX_PARALLELISM_ACTIVE)
(src/shims.h), which calls pthread_qos_max_parallelism(qos_class, flags)
and falls back to the CPU count only if that returns zero. On Apple silicon
the answer is not uniform: the background QoS class is confined to the
efficiency cluster, so its parallelism is the E-core count, not the total.
This program prints the kernel's answer for each QoS class in both the
logical and the PTHREAD_MAX_PARALLELISM_PHYSICAL flavour, and asserts the
ordering libdispatch relies on (background never wider than default).
Companion to the GCD deep-dive:
see docs/research/async-io/gcd/index.md § "dispatch_apply and QoS-aware parallelism".
Run with: dub run --single qos-parallelism.d
Portability
macOS only. pthread_qos_max_parallelism exists since macOS
10.13; on an older host it returns a negative value and the program prints a
SKIP: line and exits 0.
gcd_qos_parallelism;
import (package) stdstd.(module) std.parallelismstd.parallelism implements high-level primitives for SMP `parallelism`.
These include parallel foreach, parallel reduce, parallel eager map, pipelining
and future/promise `parallelism`. `std.`parallelism is recommended when the
same operation is to be executed in parallel on different data, or when a
function is to be executed in a background thread and its result returned to a
well-defined main thread. For communication between arbitrary threads, see
std.concurrency.
std.parallelism`` is based on the concept of a Task. A Task is an
object that represents the fundamental unit of work in this library and may be
executed in parallel with any other Task. Using Task
directly allows programming with a future/promise paradigm. All other
supported parallelism paradigms (parallel foreach, map, reduce, pipelining)
represent an additional level of abstraction over Task. They
automatically create one or more Task objects, or closely related types
that are conceptually identical but not part of the public API.
After creation, a Task may be executed in a new thread, or submitted
to a TaskPool for execution. A TaskPool encapsulates a task queue
and its worker threads. Its purpose is to efficiently map a large
number of Tasks onto a smaller number of threads. A task queue is a
FIFO queue of Task objects that have been submitted to the
TaskPool and are awaiting execution. A worker thread is a thread that
is associated with exactly one task queue. It executes the Task at the
front of its queue when the queue has work available, or sleeps when
no work is available. Each task queue is associated with zero or
more worker threads. If the result of a Task is needed before execution
by a worker thread has begun, the Task can be removed from the task queue
and executed immediately in the thread where the result is needed.
Warning
Unless marked as @trusted or @safe, artifacts in
this module allow implicit data sharing between threads and cannot
guarantee that client code is free from low level data races.
Source
std/parallelism.d
Author
David Simcha
parallelism : totalCPUs;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) gcd_qos_parallelism.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) gcd_qos_parallelism.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
extern (C) nothrow @nogc int int gcd_qos_parallelism.pthread_qos_max_parallelism(uint qosClass, ulong flags) nothrow @nogcpthread_qos_max_parallelism(uint (parameter) uint qosClassqosClass, ulong (parameter) ulong flagsflags);
/// `PTHREAD_MAX_PARALLELISM_PHYSICAL` — count physical cores, not logical ones.
enum (constant) ulong gcd_qos_parallelism.PTHREAD_MAX_PARALLELISM_PHYSICAL = 1LUPTHREAD_MAX_PARALLELISM_PHYSICAL — count physical cores, not logical ones.
PTHREAD_MAX_PARALLELISM_PHYSICAL = 0x1UL;
struct (struct) gcd_qos_parallelism.QosClassQosClass
{
(alias) object.string = stringstring (field) string gcd_qos_parallelism.QosClass.namename;
uint (field) uint gcd_qos_parallelism.QosClass.valuevalue;
}
static immutable (struct) gcd_qos_parallelism.QosClassQosClass[] (immutable global) immutable(gcd_qos_parallelism.QosClass[]) gcd_qos_parallelism.qosClassesqosClasses = [
(struct) gcd_qos_parallelism.QosClassQosClass("QOS_CLASS_BACKGROUND", 0x09),
(struct) gcd_qos_parallelism.QosClassQosClass("QOS_CLASS_UTILITY", 0x11),
(struct) gcd_qos_parallelism.QosClassQosClass("QOS_CLASS_DEFAULT", 0x15),
(struct) gcd_qos_parallelism.QosClassQosClass("QOS_CLASS_USER_INITIATED", 0x19),
(struct) gcd_qos_parallelism.QosClassQosClass("QOS_CLASS_USER_INTERACTIVE", 0x21),
];
int int D main()main()
{
const (local variable) const(int) probeprobe = int gcd_qos_parallelism.pthread_qos_max_parallelism(uint qosClass, ulong flags) nothrow @nogcpthread_qos_max_parallelism((immutable global) immutable(gcd_qos_parallelism.QosClass[]) gcd_qos_parallelism.qosClassesqosClasses[0].(field) uint gcd_qos_parallelism.QosClass.valuevalue, 0);
if ((local variable) const(int) probeprobe <= 0)
{
void std.stdio.writefln!(char, const(int))(in char[] fmt, const(int) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: pthread_qos_max_parallelism unavailable (returned %d)", (local variable) const(int) probeprobe);
return 0;
}
void std.stdio.writefln!(char, immutable(uint))(in char[] fmt, immutable(uint) __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("std.parallelism.totalCPUs = %d", totalCPUs);
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("QoS class logical physical");
int (local variable) int backgroundbackground, (local variable) int dfltdflt;
foreach ((parameter) immutable(gcd_qos_parallelism.QosClass) qosqos; (immutable global) immutable(gcd_qos_parallelism.QosClass[]) gcd_qos_parallelism.qosClassesqosClasses)
{
const (local variable) const(int) logicallogical = int gcd_qos_parallelism.pthread_qos_max_parallelism(uint qosClass, ulong flags) nothrow @nogcpthread_qos_max_parallelism((local variable) immutable(gcd_qos_parallelism.QosClass) qosqos.(field) uint gcd_qos_parallelism.QosClass.valuevalue, 0);
const (local variable) const(int) physicalphysical = int gcd_qos_parallelism.pthread_qos_max_parallelism(uint qosClass, ulong flags) nothrow @nogcpthread_qos_max_parallelism((local variable) immutable(gcd_qos_parallelism.QosClass) qosqos.(field) uint gcd_qos_parallelism.QosClass.valuevalue, (constant) ulong gcd_qos_parallelism.PTHREAD_MAX_PARALLELISM_PHYSICAL = 1LUPTHREAD_MAX_PARALLELISM_PHYSICAL — count physical cores, not logical ones.
PTHREAD_MAX_PARALLELISM_PHYSICAL);
void std.stdio.writefln!(char, string, const(int), const(int))(in char[] fmt, string __param_1, const(int) __param_2, const(int) __param_3) @safeEquivalent to writef(fmt, args, '\n').
writefln("%-29s %7d %8d", (local variable) immutable(gcd_qos_parallelism.QosClass) qosqos.(field) string gcd_qos_parallelism.QosClass.namename, (local variable) const(int) logicallogical, (local variable) const(int) physicalphysical);
assert((local variable) const(int) logicallogical > 0 && (local variable) const(int) physicalphysical > 0, "kernel reported non-positive parallelism");
assert((local variable) const(int) physicalphysical <= (local variable) const(int) logicallogical, "physical parallelism exceeded logical");
if ((local variable) immutable(gcd_qos_parallelism.QosClass) qosqos.(field) string gcd_qos_parallelism.QosClass.namename == "QOS_CLASS_BACKGROUND")
(local variable) int backgroundbackground = (local variable) const(int) logicallogical;
else if ((local variable) immutable(gcd_qos_parallelism.QosClass) qosqos.(field) string gcd_qos_parallelism.QosClass.namename == "QOS_CLASS_DEFAULT")
(local variable) int dfltdflt = (local variable) const(int) logicallogical;
}
// The invariant libdispatch's apply sizing depends on: a lower QoS class is
// never granted more parallelism than a higher one. On an asymmetric
// (P-core/E-core) machine this is a strict inequality.
assert((local variable) int backgroundbackground <= (local variable) int dfltdflt, "background QoS was wider than default QoS");
void std.stdio.writeln!()() @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln();
if ((local variable) int backgroundbackground < (local variable) int dfltdflt)
void std.stdio.writefln!(char, int, int)(in char[] fmt, int __param_1, int __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("asymmetric host: background QoS is confined to %d of %d cores", (local variable) int backgroundbackground, (local variable) int dfltdflt);
else
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("symmetric host: every QoS class gets %d-way parallelism", (local variable) int dfltdflt);
return 0;
}