diff --git a/Assets/Programmierparadigmen-Aktormodell.png b/Assets/Programmierparadigmen-Aktormodell.png new file mode 100644 index 0000000..8127f16 Binary files /dev/null and b/Assets/Programmierparadigmen-Aktormodell.png differ diff --git a/Assets/Programmierparadigmen-Architekturen.png b/Assets/Programmierparadigmen-Architekturen.png new file mode 100644 index 0000000..1e2415f Binary files /dev/null and b/Assets/Programmierparadigmen-Architekturen.png differ diff --git a/Assets/Programmierparadigmen-Arithmetische-Operationen-2.png b/Assets/Programmierparadigmen-Arithmetische-Operationen-2.png new file mode 100644 index 0000000..6e7ff43 Binary files /dev/null and b/Assets/Programmierparadigmen-Arithmetische-Operationen-2.png differ diff --git a/Assets/Programmierparadigmen-Arithmetische-Operationen.png b/Assets/Programmierparadigmen-Arithmetische-Operationen.png new file mode 100644 index 0000000..e58d17c Binary files /dev/null and b/Assets/Programmierparadigmen-Arithmetische-Operationen.png differ diff --git a/Assets/Programmierparadigmen-CPU-vs-GPU.png b/Assets/Programmierparadigmen-CPU-vs-GPU.png new file mode 100644 index 0000000..c7f68b6 Binary files /dev/null and b/Assets/Programmierparadigmen-CPU-vs-GPU.png differ diff --git a/Assets/Programmierparadigmen-Datenparallelität.png b/Assets/Programmierparadigmen-Datenparallelität.png new file mode 100644 index 0000000..2f9fb79 Binary files /dev/null and b/Assets/Programmierparadigmen-Datenparallelität.png differ diff --git a/Assets/Programmierparadigmen-Instruktionsparallelität.png b/Assets/Programmierparadigmen-Instruktionsparallelität.png new file mode 100644 index 0000000..8ab2d7d Binary files /dev/null and b/Assets/Programmierparadigmen-Instruktionsparallelität.png differ diff --git a/Assets/Programmierparadigmen-Lambda_Abstraktion.png b/Assets/Programmierparadigmen-Lambda_Abstraktion.png new file mode 100644 index 0000000..a257825 Binary files /dev/null and b/Assets/Programmierparadigmen-Lambda_Abstraktion.png differ diff --git a/Assets/Programmierparadigmen-Multicore-Systeme.png b/Assets/Programmierparadigmen-Multicore-Systeme.png new file mode 100644 index 0000000..54a8ba5 Binary files /dev/null and b/Assets/Programmierparadigmen-Multicore-Systeme.png differ diff --git a/Assets/Programmierparadigmen-Multiprozessorsysteme.png b/Assets/Programmierparadigmen-Multiprozessorsysteme.png new file mode 100644 index 0000000..db33c70 Binary files /dev/null and b/Assets/Programmierparadigmen-Multiprozessorsysteme.png differ diff --git a/Assets/Programmierparadigmen-NUMA.png b/Assets/Programmierparadigmen-NUMA.png new file mode 100644 index 0000000..7361ee8 Binary files /dev/null and b/Assets/Programmierparadigmen-NUMA.png differ diff --git a/Assets/Programmierparadigmen-Programmiermodelle.png b/Assets/Programmierparadigmen-Programmiermodelle.png new file mode 100644 index 0000000..25d0aa3 Binary files /dev/null and b/Assets/Programmierparadigmen-Programmiermodelle.png differ diff --git a/Assets/Programmierparadigmen-SMP Erlang.png b/Assets/Programmierparadigmen-SMP Erlang.png new file mode 100644 index 0000000..883fc2c Binary files /dev/null and b/Assets/Programmierparadigmen-SMP Erlang.png differ diff --git a/Assets/Programmierparadigmen-SMP.png b/Assets/Programmierparadigmen-SMP.png new file mode 100644 index 0000000..c63d3db Binary files /dev/null and b/Assets/Programmierparadigmen-SMP.png differ diff --git a/Assets/Programmierparadigmen-Shared-Memory-vs-Message-Passing.png b/Assets/Programmierparadigmen-Shared-Memory-vs-Message-Passing.png new file mode 100644 index 0000000..ba58aa3 Binary files /dev/null and b/Assets/Programmierparadigmen-Shared-Memory-vs-Message-Passing.png differ diff --git a/Assets/Programmierparadigmen-Speedup-Bestimmung.png b/Assets/Programmierparadigmen-Speedup-Bestimmung.png new file mode 100644 index 0000000..0b76e3f Binary files /dev/null and b/Assets/Programmierparadigmen-Speedup-Bestimmung.png differ diff --git a/Assets/Programmierparadigmen-Speedup-Diskussion.png b/Assets/Programmierparadigmen-Speedup-Diskussion.png new file mode 100644 index 0000000..7a568ad Binary files /dev/null and b/Assets/Programmierparadigmen-Speedup-Diskussion.png differ diff --git a/Assets/Programmierparadigmen-Taskparallelität.png b/Assets/Programmierparadigmen-Taskparallelität.png new file mode 100644 index 0000000..1fc4efa Binary files /dev/null and b/Assets/Programmierparadigmen-Taskparallelität.png differ diff --git a/Assets/Programmierparadigmen-alternate-bit-protokoll.png b/Assets/Programmierparadigmen-alternate-bit-protokoll.png new file mode 100644 index 0000000..79561b4 Binary files /dev/null and b/Assets/Programmierparadigmen-alternate-bit-protokoll.png differ diff --git a/Assets/Programmierparadigmen-amdahlsches-gesetz.png b/Assets/Programmierparadigmen-amdahlsches-gesetz.png new file mode 100644 index 0000000..8e5503e Binary files /dev/null and b/Assets/Programmierparadigmen-amdahlsches-gesetz.png differ diff --git a/Assets/Programmierparadigmen-asynchron-empfangen.png b/Assets/Programmierparadigmen-asynchron-empfangen.png new file mode 100644 index 0000000..e96970e Binary files /dev/null and b/Assets/Programmierparadigmen-asynchron-empfangen.png differ diff --git a/Assets/Programmierparadigmen-asynchrones-senden.png b/Assets/Programmierparadigmen-asynchrones-senden.png new file mode 100644 index 0000000..5506a86 Binary files /dev/null and b/Assets/Programmierparadigmen-asynchrones-senden.png differ diff --git a/Assets/Programmierparadigmen-aws-lambda-designer.png b/Assets/Programmierparadigmen-aws-lambda-designer.png new file mode 100644 index 0000000..f7cb0e2 Binary files /dev/null and b/Assets/Programmierparadigmen-aws-lambda-designer.png differ diff --git a/Assets/Programmierparadigmen-aws-produkte.png b/Assets/Programmierparadigmen-aws-produkte.png new file mode 100644 index 0000000..c7af813 Binary files /dev/null and b/Assets/Programmierparadigmen-aws-produkte.png differ diff --git a/Assets/Programmierparadigmen-cloud-architekturen.png b/Assets/Programmierparadigmen-cloud-architekturen.png new file mode 100644 index 0000000..efeff11 Binary files /dev/null and b/Assets/Programmierparadigmen-cloud-architekturen.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-01.png b/Assets/Programmierparadigmen-code-snippet-01.png new file mode 100644 index 0000000..4e84cb2 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-01.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-02.png b/Assets/Programmierparadigmen-code-snippet-02.png new file mode 100644 index 0000000..79ed45c Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-02.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-03.png b/Assets/Programmierparadigmen-code-snippet-03.png new file mode 100644 index 0000000..7b963ce Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-03.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-04.png b/Assets/Programmierparadigmen-code-snippet-04.png new file mode 100644 index 0000000..5ab5c3a Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-04.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-05.png b/Assets/Programmierparadigmen-code-snippet-05.png new file mode 100644 index 0000000..c446639 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-05.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-06.png b/Assets/Programmierparadigmen-code-snippet-06.png new file mode 100644 index 0000000..db65bbf Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-06.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-07.png b/Assets/Programmierparadigmen-code-snippet-07.png new file mode 100644 index 0000000..8757101 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-07.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-08.png b/Assets/Programmierparadigmen-code-snippet-08.png new file mode 100644 index 0000000..8498463 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-08.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-09.png b/Assets/Programmierparadigmen-code-snippet-09.png new file mode 100644 index 0000000..1b5a62d Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-09.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-10.png b/Assets/Programmierparadigmen-code-snippet-10.png new file mode 100644 index 0000000..ea9dc4e Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-10.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-11.png b/Assets/Programmierparadigmen-code-snippet-11.png new file mode 100644 index 0000000..2d1428f Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-11.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-12.png b/Assets/Programmierparadigmen-code-snippet-12.png new file mode 100644 index 0000000..3835f61 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-12.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-13.png b/Assets/Programmierparadigmen-code-snippet-13.png new file mode 100644 index 0000000..385256a Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-13.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-14.png b/Assets/Programmierparadigmen-code-snippet-14.png new file mode 100644 index 0000000..5ce4e6a Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-14.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-15.png b/Assets/Programmierparadigmen-code-snippet-15.png new file mode 100644 index 0000000..7f32c88 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-15.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-16.png b/Assets/Programmierparadigmen-code-snippet-16.png new file mode 100644 index 0000000..a6939c0 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-16.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-17.png b/Assets/Programmierparadigmen-code-snippet-17.png new file mode 100644 index 0000000..70c2977 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-17.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-18.png b/Assets/Programmierparadigmen-code-snippet-18.png new file mode 100644 index 0000000..be73b13 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-18.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-19.png b/Assets/Programmierparadigmen-code-snippet-19.png new file mode 100644 index 0000000..a6f32c1 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-19.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-20.png b/Assets/Programmierparadigmen-code-snippet-20.png new file mode 100644 index 0000000..7a1c732 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-20.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-21.png b/Assets/Programmierparadigmen-code-snippet-21.png new file mode 100644 index 0000000..5a63542 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-21.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-22.png b/Assets/Programmierparadigmen-code-snippet-22.png new file mode 100644 index 0000000..0897cc4 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-22.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-23.png b/Assets/Programmierparadigmen-code-snippet-23.png new file mode 100644 index 0000000..2458c77 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-23.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-24.png b/Assets/Programmierparadigmen-code-snippet-24.png new file mode 100644 index 0000000..adb1a73 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-24.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-25.png b/Assets/Programmierparadigmen-code-snippet-25.png new file mode 100644 index 0000000..c291953 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-25.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-26.png b/Assets/Programmierparadigmen-code-snippet-26.png new file mode 100644 index 0000000..032b440 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-26.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-27.png b/Assets/Programmierparadigmen-code-snippet-27.png new file mode 100644 index 0000000..17d33e5 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-27.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-28.png b/Assets/Programmierparadigmen-code-snippet-28.png new file mode 100644 index 0000000..6f8ed80 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-28.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-29.png b/Assets/Programmierparadigmen-code-snippet-29.png new file mode 100644 index 0000000..c991988 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-29.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-30.png b/Assets/Programmierparadigmen-code-snippet-30.png new file mode 100644 index 0000000..8893633 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-30.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-31.png b/Assets/Programmierparadigmen-code-snippet-31.png new file mode 100644 index 0000000..4ab6adf Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-31.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-32.png b/Assets/Programmierparadigmen-code-snippet-32.png new file mode 100644 index 0000000..f245dbd Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-32.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-33.png b/Assets/Programmierparadigmen-code-snippet-33.png new file mode 100644 index 0000000..4ec08fd Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-33.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-34.png b/Assets/Programmierparadigmen-code-snippet-34.png new file mode 100644 index 0000000..955fb47 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-34.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-35.png b/Assets/Programmierparadigmen-code-snippet-35.png new file mode 100644 index 0000000..20254b7 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-35.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-36.png b/Assets/Programmierparadigmen-code-snippet-36.png new file mode 100644 index 0000000..eef0642 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-36.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-37.png b/Assets/Programmierparadigmen-code-snippet-37.png new file mode 100644 index 0000000..2efd316 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-37.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-38.png b/Assets/Programmierparadigmen-code-snippet-38.png new file mode 100644 index 0000000..5648ff1 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-38.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-39.png b/Assets/Programmierparadigmen-code-snippet-39.png new file mode 100644 index 0000000..5ed3c77 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-39.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-40.png b/Assets/Programmierparadigmen-code-snippet-40.png new file mode 100644 index 0000000..31e31f2 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-40.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-41.png b/Assets/Programmierparadigmen-code-snippet-41.png new file mode 100644 index 0000000..da47508 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-41.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-42.png b/Assets/Programmierparadigmen-code-snippet-42.png new file mode 100644 index 0000000..c97d69e Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-42.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-43.png b/Assets/Programmierparadigmen-code-snippet-43.png new file mode 100644 index 0000000..ff7cce7 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-43.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-44.png b/Assets/Programmierparadigmen-code-snippet-44.png new file mode 100644 index 0000000..1aedff0 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-44.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-45.png b/Assets/Programmierparadigmen-code-snippet-45.png new file mode 100644 index 0000000..a115a34 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-45.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-46.png b/Assets/Programmierparadigmen-code-snippet-46.png new file mode 100644 index 0000000..f686605 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-46.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-47.png b/Assets/Programmierparadigmen-code-snippet-47.png new file mode 100644 index 0000000..dfd3d16 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-47.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-48.png b/Assets/Programmierparadigmen-code-snippet-48.png new file mode 100644 index 0000000..e62f537 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-48.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-49.png b/Assets/Programmierparadigmen-code-snippet-49.png new file mode 100644 index 0000000..08d6a71 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-49.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-50.png b/Assets/Programmierparadigmen-code-snippet-50.png new file mode 100644 index 0000000..7a4f7cb Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-50.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-51.png b/Assets/Programmierparadigmen-code-snippet-51.png new file mode 100644 index 0000000..497d6be Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-51.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-52.png b/Assets/Programmierparadigmen-code-snippet-52.png new file mode 100644 index 0000000..4d6b100 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-52.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-53.png b/Assets/Programmierparadigmen-code-snippet-53.png new file mode 100644 index 0000000..7853e2c Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-53.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-54.png b/Assets/Programmierparadigmen-code-snippet-54.png new file mode 100644 index 0000000..8ddcad3 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-54.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-55.png b/Assets/Programmierparadigmen-code-snippet-55.png new file mode 100644 index 0000000..1f7f106 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-55.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-56.png b/Assets/Programmierparadigmen-code-snippet-56.png new file mode 100644 index 0000000..3e6e3d6 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-56.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-57.png b/Assets/Programmierparadigmen-code-snippet-57.png new file mode 100644 index 0000000..bde7dcb Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-57.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-58.png b/Assets/Programmierparadigmen-code-snippet-58.png new file mode 100644 index 0000000..09ec3c0 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-58.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-59.png b/Assets/Programmierparadigmen-code-snippet-59.png new file mode 100644 index 0000000..f4f7bbf Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-59.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-60.png b/Assets/Programmierparadigmen-code-snippet-60.png new file mode 100644 index 0000000..3b3d501 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-60.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-61.png b/Assets/Programmierparadigmen-code-snippet-61.png new file mode 100644 index 0000000..c9517bd Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-61.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-62.png b/Assets/Programmierparadigmen-code-snippet-62.png new file mode 100644 index 0000000..3f70e4d Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-62.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-63.png b/Assets/Programmierparadigmen-code-snippet-63.png new file mode 100644 index 0000000..b0480c8 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-63.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-64.png b/Assets/Programmierparadigmen-code-snippet-64.png new file mode 100644 index 0000000..8ca1f98 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-64.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-65.png b/Assets/Programmierparadigmen-code-snippet-65.png new file mode 100644 index 0000000..aca802c Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-65.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-66.png b/Assets/Programmierparadigmen-code-snippet-66.png new file mode 100644 index 0000000..fec1247 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-66.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-67.png b/Assets/Programmierparadigmen-code-snippet-67.png new file mode 100644 index 0000000..4c89954 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-67.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-68.png b/Assets/Programmierparadigmen-code-snippet-68.png new file mode 100644 index 0000000..993fe97 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-68.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-69.png b/Assets/Programmierparadigmen-code-snippet-69.png new file mode 100644 index 0000000..3ae1859 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-69.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-70.png b/Assets/Programmierparadigmen-code-snippet-70.png new file mode 100644 index 0000000..49f5efc Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-70.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-71.png b/Assets/Programmierparadigmen-code-snippet-71.png new file mode 100644 index 0000000..bf86590 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-71.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-72.png b/Assets/Programmierparadigmen-code-snippet-72.png new file mode 100644 index 0000000..1984f5c Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-72.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-73.png b/Assets/Programmierparadigmen-code-snippet-73.png new file mode 100644 index 0000000..711ef3c Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-73.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-74.png b/Assets/Programmierparadigmen-code-snippet-74.png new file mode 100644 index 0000000..24390a4 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-74.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-75.png b/Assets/Programmierparadigmen-code-snippet-75.png new file mode 100644 index 0000000..90afd78 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-75.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-76.png b/Assets/Programmierparadigmen-code-snippet-76.png new file mode 100644 index 0000000..13f17a2 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-76.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-77.png b/Assets/Programmierparadigmen-code-snippet-77.png new file mode 100644 index 0000000..5274271 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-77.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-78.png b/Assets/Programmierparadigmen-code-snippet-78.png new file mode 100644 index 0000000..62010e8 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-78.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-79.png b/Assets/Programmierparadigmen-code-snippet-79.png new file mode 100644 index 0000000..d7ac84f Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-79.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-80.png b/Assets/Programmierparadigmen-code-snippet-80.png new file mode 100644 index 0000000..3ceaba6 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-80.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-81.png b/Assets/Programmierparadigmen-code-snippet-81.png new file mode 100644 index 0000000..69a87f0 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-81.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-82.png b/Assets/Programmierparadigmen-code-snippet-82.png new file mode 100644 index 0000000..42ade62 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-82.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-83.png b/Assets/Programmierparadigmen-code-snippet-83.png new file mode 100644 index 0000000..0266488 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-83.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-84.png b/Assets/Programmierparadigmen-code-snippet-84.png new file mode 100644 index 0000000..03f2c9b Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-84.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-85.png b/Assets/Programmierparadigmen-code-snippet-85.png new file mode 100644 index 0000000..1199311 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-85.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-86.png b/Assets/Programmierparadigmen-code-snippet-86.png new file mode 100644 index 0000000..c5686e2 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-86.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-87.png b/Assets/Programmierparadigmen-code-snippet-87.png new file mode 100644 index 0000000..8984c7b Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-87.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-88.png b/Assets/Programmierparadigmen-code-snippet-88.png new file mode 100644 index 0000000..93db0b5 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-88.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-89.png b/Assets/Programmierparadigmen-code-snippet-89.png new file mode 100644 index 0000000..b33c1b6 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-89.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-90.png b/Assets/Programmierparadigmen-code-snippet-90.png new file mode 100644 index 0000000..c5797d2 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-90.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-91.png b/Assets/Programmierparadigmen-code-snippet-91.png new file mode 100644 index 0000000..b77d714 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-91.png differ diff --git a/Assets/Programmierparadigmen-code-snippet-92.png b/Assets/Programmierparadigmen-code-snippet-92.png new file mode 100644 index 0000000..3232507 Binary files /dev/null and b/Assets/Programmierparadigmen-code-snippet-92.png differ diff --git a/Assets/Programmierparadigmen-diamant-beispiel.png b/Assets/Programmierparadigmen-diamant-beispiel.png new file mode 100644 index 0000000..72f55d3 Binary files /dev/null and b/Assets/Programmierparadigmen-diamant-beispiel.png differ diff --git a/Assets/Programmierparadigmen-diamant-eigenschaft.png b/Assets/Programmierparadigmen-diamant-eigenschaft.png new file mode 100644 index 0000000..063ee47 Binary files /dev/null and b/Assets/Programmierparadigmen-diamant-eigenschaft.png differ diff --git a/Assets/Programmierparadigmen-einordnung-programmierung.png b/Assets/Programmierparadigmen-einordnung-programmierung.png new file mode 100644 index 0000000..c6ee18e Binary files /dev/null and b/Assets/Programmierparadigmen-einordnung-programmierung.png differ diff --git a/Assets/Programmierparadigmen-ereignisbasiertes-modell.png b/Assets/Programmierparadigmen-ereignisbasiertes-modell.png new file mode 100644 index 0000000..3d82a04 Binary files /dev/null and b/Assets/Programmierparadigmen-ereignisbasiertes-modell.png differ diff --git a/Assets/Programmierparadigmen-erlang-Beispiele.png b/Assets/Programmierparadigmen-erlang-Beispiele.png new file mode 100644 index 0000000..ede99af Binary files /dev/null and b/Assets/Programmierparadigmen-erlang-Beispiele.png differ diff --git a/Assets/Programmierparadigmen-erlang-map.png b/Assets/Programmierparadigmen-erlang-map.png new file mode 100644 index 0000000..c8258b8 Binary files /dev/null and b/Assets/Programmierparadigmen-erlang-map.png differ diff --git a/Assets/Programmierparadigmen-erlang-reduce-parallel.png b/Assets/Programmierparadigmen-erlang-reduce-parallel.png new file mode 100644 index 0000000..c9df5e8 Binary files /dev/null and b/Assets/Programmierparadigmen-erlang-reduce-parallel.png differ diff --git a/Assets/Programmierparadigmen-erlang-reduce.png b/Assets/Programmierparadigmen-erlang-reduce.png new file mode 100644 index 0000000..ef0a1bc Binary files /dev/null and b/Assets/Programmierparadigmen-erlang-reduce.png differ diff --git a/Assets/Programmierparadigmen-erlang-scheduler.png b/Assets/Programmierparadigmen-erlang-scheduler.png new file mode 100644 index 0000000..6badeb3 Binary files /dev/null and b/Assets/Programmierparadigmen-erlang-scheduler.png differ diff --git a/Assets/Programmierparadigmen-erlang-überwachung.png b/Assets/Programmierparadigmen-erlang-überwachung.png new file mode 100644 index 0000000..29591f0 Binary files /dev/null and b/Assets/Programmierparadigmen-erlang-überwachung.png differ diff --git a/Assets/Programmierparadigmen-flynn-architekturklassifikation.png b/Assets/Programmierparadigmen-flynn-architekturklassifikation.png new file mode 100644 index 0000000..bbec0a2 Binary files /dev/null and b/Assets/Programmierparadigmen-flynn-architekturklassifikation.png differ diff --git a/Assets/Programmierparadigmen-future-task.png b/Assets/Programmierparadigmen-future-task.png new file mode 100644 index 0000000..86d8e3e Binary files /dev/null and b/Assets/Programmierparadigmen-future-task.png differ diff --git a/Assets/Programmierparadigmen-grpc.png b/Assets/Programmierparadigmen-grpc.png new file mode 100644 index 0000000..c75ea88 Binary files /dev/null and b/Assets/Programmierparadigmen-grpc.png differ diff --git a/Assets/Programmierparadigmen-java-synchronized.png b/Assets/Programmierparadigmen-java-synchronized.png new file mode 100644 index 0000000..a648ccb Binary files /dev/null and b/Assets/Programmierparadigmen-java-synchronized.png differ diff --git a/Assets/Programmierparadigmen-javaSpaces.png b/Assets/Programmierparadigmen-javaSpaces.png new file mode 100644 index 0000000..fec0fb7 Binary files /dev/null and b/Assets/Programmierparadigmen-javaSpaces.png differ diff --git a/Assets/Programmierparadigmen-kodierung-natürlicher-zahlen.png b/Assets/Programmierparadigmen-kodierung-natürlicher-zahlen.png new file mode 100644 index 0000000..04d2237 Binary files /dev/null and b/Assets/Programmierparadigmen-kodierung-natürlicher-zahlen.png differ diff --git a/Assets/Programmierparadigmen-kommunikation-fehler-2.png b/Assets/Programmierparadigmen-kommunikation-fehler-2.png new file mode 100644 index 0000000..62d64bd Binary files /dev/null and b/Assets/Programmierparadigmen-kommunikation-fehler-2.png differ diff --git a/Assets/Programmierparadigmen-kommunikation-fehler.png b/Assets/Programmierparadigmen-kommunikation-fehler.png new file mode 100644 index 0000000..34ea880 Binary files /dev/null and b/Assets/Programmierparadigmen-kommunikation-fehler.png differ diff --git a/Assets/Programmierparadigmen-kommunikation.png b/Assets/Programmierparadigmen-kommunikation.png new file mode 100644 index 0000000..45b9b34 Binary files /dev/null and b/Assets/Programmierparadigmen-kommunikation.png differ diff --git a/Assets/Programmierparadigmen-master-worker-thread.png b/Assets/Programmierparadigmen-master-worker-thread.png new file mode 100644 index 0000000..4ea5b68 Binary files /dev/null and b/Assets/Programmierparadigmen-master-worker-thread.png differ diff --git a/Assets/Programmierparadigmen-microservice.png b/Assets/Programmierparadigmen-microservice.png new file mode 100644 index 0000000..8f88c5f Binary files /dev/null and b/Assets/Programmierparadigmen-microservice.png differ diff --git a/Assets/Programmierparadigmen-mutex-c.png b/Assets/Programmierparadigmen-mutex-c.png new file mode 100644 index 0000000..21306de Binary files /dev/null and b/Assets/Programmierparadigmen-mutex-c.png differ diff --git a/Assets/Programmierparadigmen-netzwerk-stubs.png b/Assets/Programmierparadigmen-netzwerk-stubs.png new file mode 100644 index 0000000..80d7769 Binary files /dev/null and b/Assets/Programmierparadigmen-netzwerk-stubs.png differ diff --git a/Assets/Programmierparadigmen-parallelisierung.png b/Assets/Programmierparadigmen-parallelisierung.png new file mode 100644 index 0000000..5734211 Binary files /dev/null and b/Assets/Programmierparadigmen-parallelisierung.png differ diff --git a/Assets/Programmierparadigmen-philosophen.png b/Assets/Programmierparadigmen-philosophen.png new file mode 100644 index 0000000..c485c91 Binary files /dev/null and b/Assets/Programmierparadigmen-philosophen.png differ diff --git a/Assets/Programmierparadigmen-rabbitmq.png b/Assets/Programmierparadigmen-rabbitmq.png new file mode 100644 index 0000000..fbeca85 Binary files /dev/null and b/Assets/Programmierparadigmen-rabbitmq.png differ diff --git a/Assets/Programmierparadigmen-spotify-api.png b/Assets/Programmierparadigmen-spotify-api.png new file mode 100644 index 0000000..4dced34 Binary files /dev/null and b/Assets/Programmierparadigmen-spotify-api.png differ diff --git a/Assets/Programmierparadigmen-springio.png b/Assets/Programmierparadigmen-springio.png new file mode 100644 index 0000000..46e7596 Binary files /dev/null and b/Assets/Programmierparadigmen-springio.png differ diff --git a/Assets/Programmierparadigmen-synchron-empfangen.png b/Assets/Programmierparadigmen-synchron-empfangen.png new file mode 100644 index 0000000..ec9121a Binary files /dev/null and b/Assets/Programmierparadigmen-synchron-empfangen.png differ diff --git a/Assets/Programmierparadigmen-synchrones-senden.png b/Assets/Programmierparadigmen-synchrones-senden.png new file mode 100644 index 0000000..f91e24a Binary files /dev/null and b/Assets/Programmierparadigmen-synchrones-senden.png differ diff --git a/Assets/Programmierparadigmen-zustände-bit-protokoll.png b/Assets/Programmierparadigmen-zustände-bit-protokoll.png new file mode 100644 index 0000000..6d638bd Binary files /dev/null and b/Assets/Programmierparadigmen-zustände-bit-protokoll.png differ diff --git a/Programmierparadigmen.pdf b/Programmierparadigmen.pdf index 76538ae..b537e30 100644 Binary files a/Programmierparadigmen.pdf and b/Programmierparadigmen.pdf differ diff --git a/Programmierparadigmen.tex b/Programmierparadigmen.tex index 60c7080..1d84487 100644 --- a/Programmierparadigmen.tex +++ b/Programmierparadigmen.tex @@ -2312,7 +2312,7 @@ Innere Abstraktionen können äußere Variablen verdecken: $$(\lambda x.\lambda y.\lambda z.f( \lambda x.z+x)(y x)) (\lambda y.y+x)$$ \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-Lambda_Abstraktion} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Lambda_Abstraktion} \end{center} \subsubsection{Freie und gebundene Variablen} @@ -2359,1204 +2359,1204 @@ Beispiel: let \\ Beispiel: let x = g y in f x berechnet f(g y)\\ \hspace*{14.5mm} ($\lambda x.fx)(g y) \Rightarrow f(g y)$ - \subsection{Äquivalenz} - \paragraph{$\alpha$-Äquivalenz} - - Namen gebundener Variablen - \begin{itemize*} - \item dienen letztlich nur der Dokumentation - \item entscheidend sind die Bindungen - \end{itemize*} - - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - $\alpha$-Äquivalenz \\ - $ t_1$ und $t_2$ heißen $\alpha$-Äquivalent ($t_1 \stackrel{\alpha}{=} t_2$), wenn $t_1$ in $t_2$ durch konsistente Umbenennung der $\lambda$-gebundenen Variablen überführt werden kann. - \end{minipage} - } - - Beispiele: - \begin{center} - $$\lambda x.x \stackrel{\alpha}{=} \lambda y.y$$ - $$\lambda x.\lambda z.f(\lambda y.zy)x \stackrel{\alpha}{=} \lambda y.\lambda x.f(\lambda z.xz)y$$ - \end{center} - aber - \begin{center} - $$\lambda x.\lambda z.f(\lambda y.zy)x \stackrel{\alpha}{\neq} \lambda x.\lambda z.g(\lambda y.zy)x$$ - $$\lambda z.\lambda z.f(\lambda y.zy)z\stackrel{\alpha}{\neq} \lambda x.\lambda z.f(\lambda y.zy)x$$ - \end{center} - - \paragraph{$\eta$-Äquivalenz} - - Extensionalitäts-Prinzip: - \begin{itemize*} - \item Zwei Funktionen sind gleich, falls Ergebnis gleich für alle Argumente - \end{itemize*} - \colorbox{lightgray} { - \begin{minipage}[h]{1.0\linewidth} - $\eta$-Äquivalenz \\ - Terme $\lambda$x.fx und f heißen $\eta$-äquivalent ($\lambda$x.fx $\stackrel{\eta}{=}$ f), falls x nicht freie Variable von f ist. - \end{minipage} - } - - Beispiele: - $$\lambda x.\lambda y.f z x y \stackrel{\eta}{=}\lambda x.f z x$$ - $$f z \stackrel{\eta}{=}\lambda x.f z x$$ - $$\lambda x.x \stackrel{\eta}{=}\lambda x.(\lambda x.x)x$$ - aber $$\lambda x.f x x \stackrel{\eta}{\neq} f x$$ - - \subsection{Kodierung boolscher Werte} - Church Booleans - \begin{itemize*} - \item True wird zu: $C_{true} = \lambda t.\lambda f.t$ - \item False wird zu: $C_{false} = \lambda t.\lambda f.f$ - \item If-then-else wird zu: $If = \lambda a.a$ - \end{itemize*} - - Beispiel - \begin{lstlisting} +\subsection{Äquivalenz} +\paragraph{$\alpha$-Äquivalenz} + +Namen gebundener Variablen +\begin{itemize*} + \item dienen letztlich nur der Dokumentation + \item entscheidend sind die Bindungen +\end{itemize*} + +\colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + $\alpha$-Äquivalenz \\ + $ t_1$ und $t_2$ heißen $\alpha$-Äquivalent ($t_1 \stackrel{\alpha}{=} t_2$), wenn $t_1$ in $t_2$ durch konsistente Umbenennung der $\lambda$-gebundenen Variablen überführt werden kann. + \end{minipage} +} + +Beispiele: +\begin{center} + $$\lambda x.x \stackrel{\alpha}{=} \lambda y.y$$ + $$\lambda x.\lambda z.f(\lambda y.zy)x \stackrel{\alpha}{=} \lambda y.\lambda x.f(\lambda z.xz)y$$ +\end{center} +aber +\begin{center} + $$\lambda x.\lambda z.f(\lambda y.zy)x \stackrel{\alpha}{\neq} \lambda x.\lambda z.g(\lambda y.zy)x$$ + $$\lambda z.\lambda z.f(\lambda y.zy)z\stackrel{\alpha}{\neq} \lambda x.\lambda z.f(\lambda y.zy)x$$ +\end{center} + +\paragraph{$\eta$-Äquivalenz} + +Extensionalitäts-Prinzip: +\begin{itemize*} + \item Zwei Funktionen sind gleich, falls Ergebnis gleich für alle Argumente +\end{itemize*} +\colorbox{lightgray} { + \begin{minipage}[h]{1.0\linewidth} + $\eta$-Äquivalenz \\ + Terme $\lambda$x.fx und f heißen $\eta$-äquivalent ($\lambda$x.fx $\stackrel{\eta}{=}$ f), falls x nicht freie Variable von f ist. + \end{minipage} +} + +Beispiele: +$$\lambda x.\lambda y.f z x y \stackrel{\eta}{=}\lambda x.f z x$$ +$$f z \stackrel{\eta}{=}\lambda x.f z x$$ +$$\lambda x.x \stackrel{\eta}{=}\lambda x.(\lambda x.x)x$$ +aber $$\lambda x.f x x \stackrel{\eta}{\neq} f x$$ + +\subsection{Kodierung boolscher Werte} +Church Booleans +\begin{itemize*} + \item True wird zu: $C_{true} = \lambda t.\lambda f.t$ + \item False wird zu: $C_{false} = \lambda t.\lambda f.f$ + \item If-then-else wird zu: $If = \lambda a.a$ +\end{itemize*} + +Beispiel +\begin{lstlisting} if True then x else y \end{lstlisting} - ergibt: $(\lambda a.a)(\lambda t- \lambda f.t) x y = (\lambda t.\lambda f.t) xy = (\lambda f.x)y \Rightarrow x$ +ergibt: $(\lambda a.a)(\lambda t- \lambda f.t) x y = (\lambda t.\lambda f.t) xy = (\lambda f.x)y \Rightarrow x$ - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Lambda_Abstraktion.png} - \end{center} - - \begin{itemize*} - \item if True then x else y ergibt: - \subitem ($\lambda$\color{blue}a.a\color{black})(\color{red} $\lambda$t.$\lambda$f.t\color{black}) x y $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black}) \color{red}x\color{black} y $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.x)\color{red}y\color{black} $\Rightarrow$ x - \item $b_1$ \&\& $b_2$ ist äquivalent zu if $b_1$ then $b_2$ else False - \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ $C_false$ - \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ ($\lambda$t.$\lambda$f.f) - \item True \&\& True ergibt: - \subitem \color{white} $\Rightarrow$ \color{black}($\lambda$\color{blue}a.a\color{black})\color{red}$C_true$ \color{black} $C_true$ ($\lambda$t.$\lambda$f.f) - \subitem $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black})\color{red}($\lambda$t.$\lambda$f.t)\color{black}($\lambda$t.$\lambda$f.f) - \subitem $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.($\lambda$t.$\lambda$f.t)) \color{red}($\lambda$t.$\lambda$f.f)\color{black} $\Rightarrow$ $\lambda$t.$\lambda$f.f = $C_true$ - \item $b_1 \lor b_2$ entspricht: - \subitem if $b_1$ then True else $b_2$ - \item $\neg b_1$ entspricht: - \subitem if $b_1$ then False else True - \item $b_1 \Rightarrow b_2$ entspricht: - \subitem if $b_1$ then $b_2$ else True - \end{itemize*} - - \subsection{Kodierung natürlicher Zahlen} - Eine natürliche Zahl drückt aus, wie oft etwas geschehen soll. +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Lambda_Abstraktion.png} +\end{center} + +\begin{itemize*} + \item if True then x else y ergibt: + \subitem ($\lambda$\color{blue}a.a\color{black})(\color{red} $\lambda$t.$\lambda$f.t\color{black}) x y $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black}) \color{red}x\color{black} y $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.x)\color{red}y\color{black} $\Rightarrow$ x + \item $b_1$ \&\& $b_2$ ist äquivalent zu if $b_1$ then $b_2$ else False + \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ $C_false$ + \subitem $\Rightarrow$ $b_1$ \&\& $b_2$ wird zu ($\lambda$a.a) $b_1$ $b_2$ ($\lambda$t.$\lambda$f.f) + \item True \&\& True ergibt: + \subitem \color{white} $\Rightarrow$ \color{black}($\lambda$\color{blue}a.a\color{black})\color{red}$C_true$ \color{black} $C_true$ ($\lambda$t.$\lambda$f.f) + \subitem $\Rightarrow$ ($\lambda$\color{blue}t\color{black}.$\lambda$f.\color{blue}t\color{black})\color{red}($\lambda$t.$\lambda$f.t)\color{black}($\lambda$t.$\lambda$f.f) + \subitem $\Rightarrow$ ($\lambda$\color{blue}f\color{black}.($\lambda$t.$\lambda$f.t)) \color{red}($\lambda$t.$\lambda$f.f)\color{black} $\Rightarrow$ $\lambda$t.$\lambda$f.f = $C_true$ + \item $b_1 \lor b_2$ entspricht: + \subitem if $b_1$ then True else $b_2$ + \item $\neg b_1$ entspricht: + \subitem if $b_1$ then False else True + \item $b_1 \Rightarrow b_2$ entspricht: + \subitem if $b_1$ then $b_2$ else True +\end{itemize*} + +\subsection{Kodierung natürlicher Zahlen} +Eine natürliche Zahl drückt aus, wie oft etwas geschehen soll. $c_0 = \lambda s.\lambda z.z$; $c_1=\lambda s.\lambda z.sz$; $c_2=\lambda s.\lambda z.s(sz)$;...;$c_n=\lambda s.\lambda z.s^n z$ - Arithmetische Operationen - \begin{itemize} - \item Addition: $plus = \lambda m. \lambda n. \lambda s. \lambda z. m s (n s z)$ - \item Multiplikation: $times = \lambda m. \lambda n. \lambda s. n (m s) = \lambda m. \lambda n. \lambda s. \lambda z. n (m s) z$ - \item Exponentiation: $exp = \lambda m. \lambda n. n m = \lambda m. \lambda n. \lambda s. \lambda z. n m s z$ - \item Vorgänger: $pred = \lambda n.\lambda s.\lambda x. n (\lambda y.\lambda z. z (y s))(K x)$ - \item Subtraktion: $sub = \lambda n.\lambda m. m pred n$ - \item Nullvergleich: $isZero = \lambda n. n (\lambda x. C false ) C true$ - \end{itemize} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-kodierung-natürlicher-zahlen} - \end{center} - - succ($c_2$) = ($\lambda$\color{blue}n\color{black}.$\lambda s.\lambda z.s$ (\color{blue}n\color{black} s z)) \color{red} ($\lambda$s.$\lambda z.s (s z))$ \color{black} - \subitem $\Rightarrow\lambda s.\lambda z.s ((\lambda$\color{blue}s\color{black}.$\lambda z.$ \color{blue} s \color{black}(\color{blue}s\color{black} z)) s z) - \subitem $\Rightarrow\lambda s.\lambda z.s((\lambda$\color{blue}z\color{black}.s (s \color{blue}z\color{black}))\color{red}z\color{black}) - \subitem $\Rightarrow\lambda s.\lambda z.s(s(s z)) = c_3$ +Arithmetische Operationen +\begin{itemize} + \item Addition: $plus = \lambda m. \lambda n. \lambda s. \lambda z. m s (n s z)$ + \item Multiplikation: $times = \lambda m. \lambda n. \lambda s. n (m s) = \lambda m. \lambda n. \lambda s. \lambda z. n (m s) z$ + \item Exponentiation: $exp = \lambda m. \lambda n. n m = \lambda m. \lambda n. \lambda s. \lambda z. n m s z$ + \item Vorgänger: $pred = \lambda n.\lambda s.\lambda x. n (\lambda y.\lambda z. z (y s))(K x)$ + \item Subtraktion: $sub = \lambda n.\lambda m. m pred n$ + \item Nullvergleich: $isZero = \lambda n. n (\lambda x. C false ) C true$ +\end{itemize} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-kodierung-natürlicher-zahlen} +\end{center} - \subsection{Rechnen mit Church - Zahlen } - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-diamant-eigenschaft} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-01.png} - - Idee zu exp: \\ - \subitem exp $c_m c_n \Rightarrow c_n c_m \Rightarrow (\lambda.\color{blue}s\color{black}.\lambda z.s^n z)\color{red}(\lambda s. \lambda z.s^m z)$ \color{black} - \subsubitem $\Rightarrow \lambda z.(\lambda s. \lambda z.s^m z)^n z$ - \subitem (per Induktion über n) $\stackrel{\alpha \beta \eta}{\Rightarrow}\lambda s.\lambda z. \lambda z.{s^m}^n z = {c_m}^n$ +succ($c_2$) = ($\lambda$\color{blue}n\color{black}.$\lambda s.\lambda z.s$ (\color{blue}n\color{black} s z)) \color{red} ($\lambda$s.$\lambda z.s (s z))$ \color{black} +\subitem $\Rightarrow\lambda s.\lambda z.s ((\lambda$\color{blue}s\color{black}.$\lambda z.$ \color{blue} s \color{black}(\color{blue}s\color{black} z)) s z) +\subitem $\Rightarrow\lambda s.\lambda z.s((\lambda$\color{blue}z\color{black}.s (s \color{blue}z\color{black}))\color{red}z\color{black}) +\subitem $\Rightarrow\lambda s.\lambda z.s(s(s z)) = c_3$ + +\subsection{Rechnen mit Church - Zahlen } +\includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-diamant-eigenschaft} +\includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-01.png} + +Idee zu exp: \\ +\subitem exp $c_m c_n \Rightarrow c_n c_m \Rightarrow (\lambda.\color{blue}s\color{black}.\lambda z.s^n z)\color{red}(\lambda s. \lambda z.s^m z)$ \color{black} +\subsubitem $\Rightarrow \lambda z.(\lambda s. \lambda z.s^m z)^n z$ +\subitem (per Induktion über n) $\stackrel{\alpha \beta \eta}{\Rightarrow}\lambda s.\lambda z. \lambda z.{s^m}^n z = {c_m}^n$ + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Arithmetische-Operationen} +\end{center} + +\subitem $isZero(c_0) = (\lambda\color{blue}n\color{black}.\color{blue}n\color{black}(\lambda x.C_{false})C_{true})\color{red} (\lambda s.\lambda z.z)\color{black}$ +\subsubitem $\Rightarrow (\lambda s. \lambda z. z)\color{red} (\lambda x.C_{false})\color{black} C_{true}$ +\subsubitem $\Rightarrow (\lambda \color{blue} z.z \color{black}) \color{red} C_{true} \Rightarrow C_{true}$ +(Bemerkung: I und K sind die Identitätsfunktion bzw. das Konstanten-Funktional) + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Arithmetische-Operationen-2} +\end{center} + +\subsection{Auswertungsstrategien} +Wenn es in einem Term mehrere Redexe gibt, welchen reduziert man dann? +$$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda x.x)z))$$ +$$(\lambda \color{blue}x.x\color{black})\color{red}((\lambda x.x)(\lambda z.(\lambda x.x)z)) \color{black}$$ +$$(\lambda x.x)((\lambda \color{blue}x.x\color{black})\color{red}(\lambda z.(\lambda x.x)z)\color{black})$$ +$$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda \color{blue}x.x\color{black})\color{red}z\color{black})$$ +$$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda x.x)z))$$ + +Volle $\beta$-Reduktion: Jeder Redex kann jederzeit reduziert werden +$$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda \color{blue}x.x\color{black})\color{red}z\color{black}))$$ +$$\Rightarrow(\lambda x.x)((\lambda\color{blue}x.x\color{black})\color{red}(\lambda z.z)\color{black})$$ +$$\Rightarrow(\lambda \color{blue}x.x\color{black})\color{red}(\lambda z.z)\color{black})$$ +$$\Rightarrow(\lambda z.z)\color{black} \nRightarrow$$ +$$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda x.x)z))$$ + +Volle $\beta$-Reduktion: Jeder Redex kann jederzeit reduziert werden \\ +Normalreihenfolge: Immer der linkeste äußerste Redex wird reduziert +$$(\lambda x.x)((\lambda\color{blue}x.x\color{black})\color{red}(\lambda z.(\lambda x.x)z))\color{black}$$ +$$\Rightarrow(\lambda \color{blue}x.x\color{black})\color{red}(\lambda z.(\lambda x.x)z)\color{black}$$ +$$\Rightarrow\lambda z.(\lambda\color{blue}x.x\color{black})\color{red}z\color{black}$$ +$$\Rightarrow(\lambda z.z)\color{black}) \nRightarrow$$ + +\subsection{Fixpunktsatz und Rekursion} +\subsubsection{Divergenz} +Bisherige Beispiele werten zu einer Normalform aus. Aber: +$$\omega = (\lambda \color{blue}x.x x\color{black})\color{red}(\lambda x.x x) \color{black} \rightarrow (\lambda x.x x)(\lambda x.x x)$$ - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Arithmetische-Operationen} - \end{center} - - \subitem $isZero(c_0) = (\lambda\color{blue}n\color{black}.\color{blue}n\color{black}(\lambda x.C_{false})C_{true})\color{red} (\lambda s.\lambda z.z)\color{black}$ - \subsubitem $\Rightarrow (\lambda s. \lambda z. z)\color{red} (\lambda x.C_{false})\color{black} C_{true}$ - \subsubitem $\Rightarrow (\lambda \color{blue} z.z \color{black}) \color{red} C_{true} \Rightarrow C_{true}$ - (Bemerkung: I und K sind die Identitätsfunktion bzw. das Konstanten-Funktional) - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Arithmetische-Operationen-2} - \end{center} - - \subsection{Auswertungsstrategien} - Wenn es in einem Term mehrere Redexe gibt, welchen reduziert man dann? - $$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda x.x)z))$$ - $$(\lambda \color{blue}x.x\color{black})\color{red}((\lambda x.x)(\lambda z.(\lambda x.x)z)) \color{black}$$ - $$(\lambda x.x)((\lambda \color{blue}x.x\color{black})\color{red}(\lambda z.(\lambda x.x)z)\color{black})$$ - $$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda \color{blue}x.x\color{black})\color{red}z\color{black})$$ - $$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda x.x)z))$$ - - Volle $\beta$-Reduktion: Jeder Redex kann jederzeit reduziert werden - $$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda \color{blue}x.x\color{black})\color{red}z\color{black}))$$ - $$\Rightarrow(\lambda x.x)((\lambda\color{blue}x.x\color{black})\color{red}(\lambda z.z)\color{black})$$ - $$\Rightarrow(\lambda \color{blue}x.x\color{black})\color{red}(\lambda z.z)\color{black})$$ - $$\Rightarrow(\lambda z.z)\color{black} \nRightarrow$$ - $$(\lambda x.x)((\lambda x.x)(\lambda z.(\lambda x.x)z))$$ - - Volle $\beta$-Reduktion: Jeder Redex kann jederzeit reduziert werden \\ - Normalreihenfolge: Immer der linkeste äußerste Redex wird reduziert - $$(\lambda x.x)((\lambda\color{blue}x.x\color{black})\color{red}(\lambda z.(\lambda x.x)z))\color{black}$$ - $$\Rightarrow(\lambda \color{blue}x.x\color{black})\color{red}(\lambda z.(\lambda x.x)z)\color{black}$$ - $$\Rightarrow\lambda z.(\lambda\color{blue}x.x\color{black})\color{red}z\color{black}$$ - $$\Rightarrow(\lambda z.z)\color{black}) \nRightarrow$$ - - \subsection{Fixpunktsatz und Rekursion} - \subsubsection{Divergenz} - Bisherige Beispiele werten zu einer Normalform aus. Aber: - $$\omega = (\lambda \color{blue}x.x x\color{black})\color{red}(\lambda x.x x) \color{black} \rightarrow (\lambda x.x x)(\lambda x.x x)$$ - $\lambda x.xx$ wendet sein Argument auf das Argument selbst an $\Rightarrow$ dadurch reproduziert $\omega$ sich selbst. - - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Divergenz: \\ - Terme, die nicht zu einer Normalform auswerten, divergieren. - Diese modellieren unendliche Ausführungen. - \end{minipage} - } - - \subsubsection{Der Fixpunktsatz} - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Fixpunktsatz \\ - Für alle $F\in\Lambda$ existiert ein $X\in\lambda$ sodass gilt: $F X = X$ - \end{minipage} - } - + +\colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Divergenz: \\ + Terme, die nicht zu einer Normalform auswerten, divergieren. + Diese modellieren unendliche Ausführungen. + \end{minipage} +} + +\subsubsection{Der Fixpunktsatz} +\colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Fixpunktsatz \\ + Für alle $F\in\Lambda$ existiert ein $X\in\lambda$ sodass gilt: $F X = X$ + \end{minipage} +} + +\begin{itemize*} + \item Der Fixpunktsatz besagt, dass im Lambda-Kalkül jeder Term einen Fixpunkt hat, d.h. einen Wert, der auf sich selber abgebildet wird. + \item Beweis: \begin{itemize*} - \item Der Fixpunktsatz besagt, dass im Lambda-Kalkül jeder Term einen Fixpunkt hat, d.h. einen Wert, der auf sich selber abgebildet wird. - \item Beweis: - \begin{itemize*} - \item Zu jedem beliebigen F sei $W =\lambda x.F(x x)$ und $X = (W W)$ - \item Dann gilt: $X\equiv WW \equiv(\lambda x.F(x x)) W \equiv F(W W) \equiv F X$ - \end{itemize*} - \item Bemerkungen: - \begin{itemize*} - \item Für einige Lambda-Terme ist die Identifikation eines Fixpunktes einfach, z.B. für den Term $\lambda x.x$ (alle Terme sind Fixpunkte) - \item Für andere Terme, wie $\lambda xy.xy (= \lambda x.\lambda y.xy)$ ist das nicht so klar - \item Der Beweis des Fixpunktsatzes ist konstruiv, d.h. er liefert zu jedem Lambda-Term einen Fixpunkt - \end{itemize*} + \item Zu jedem beliebigen F sei $W =\lambda x.F(x x)$ und $X = (W W)$ + \item Dann gilt: $X\equiv WW \equiv(\lambda x.F(x x)) W \equiv F(W W) \equiv F X$ \end{itemize*} - - \subsubsection{Anwendung des Fixpunktsatzes} + \item Bemerkungen: \begin{itemize*} - \item Aufgabe: Berechne den Fixpunkt zum Term $\lambda xy.xy$ - \begin{itemize*} - \item Lösungsansatz: $W\equiv\lambda x.(\lambda\color{blue}x\color{black}y.\color{blue}x\color{black}y)\color{red}(xx) \color{black} \equiv\lambda x.\lambda y.(x x)y \equiv \lambda xy.(xx)y$ - \item Damit ist der gesuchte Fixpunkt $X\equiv((\lambda xy.(xx)y)(\lambda xy.(xx)y))$ - \item Nachrechnen: - \begin{description*} - \item[ \space ] $(\lambda xy.xy) ((\lambda xy.(xx)y) (\lambda xy.(xx)y))$ - \item[$\equiv$] $(\lambda \color{blue}x\color{black}.\lambda y. \color{blue} x \color{black} y) \color{red} ((\lambda xy.(xx)y) (\lambda xy.(xx)y)$ \color{black} - \item[$\equiv$] $\lambda \color{blue}y \color{black}.((\lambda xy.(xx)y)) (\lambda xy.(xx)y) \color{red}y\color{black}$ - \item[$\equiv$] $(\lambda xy.(xx)y)(\lambda xy.(xx)y)$ - \item[$\equiv$] X - \end{description*} - \end{itemize*} - \item Bemerkung: Der so für die Identitätsfunktion $\lambda x.x$ konstruierte Fixpunkt ist übrigens $(\lambda x.xx)(\lambda x.xx)$, er spielt die besondere Rolle des Standardterms $\bot$ für nicht-terminierende Ausführungen + \item Für einige Lambda-Terme ist die Identifikation eines Fixpunktes einfach, z.B. für den Term $\lambda x.x$ (alle Terme sind Fixpunkte) + \item Für andere Terme, wie $\lambda xy.xy (= \lambda x.\lambda y.xy)$ ist das nicht so klar + \item Der Beweis des Fixpunktsatzes ist konstruiv, d.h. er liefert zu jedem Lambda-Term einen Fixpunkt \end{itemize*} - - \subsubsection{Der Fixpunkt-Kombinator} - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Im Ergebnis unserer Diskussion des Fixpunktsatzes definieren wir den Fixpunkt-Kombinator wie folgt: $$Y \equiv \lambda f.(\lambda x.f(xx)) (\lambda x.f(xx))$$ - \end{minipage} - } +\end{itemize*} + +\subsubsection{Anwendung des Fixpunktsatzes} +\begin{itemize*} + \item Aufgabe: Berechne den Fixpunkt zum Term $\lambda xy.xy$ \begin{itemize*} - \item Dieser Kombinator spielt eine wichtige Rolle bei der Definition rekursiver Funktionen im Lambda-Kalkül, wie wir im folgenden sehen werden - \item Für jeden Lambda-Term M gilt: $Y M = M (Y M)$ - \begin{itemize*} - \item Beweisidee: zeige, dass beide Terme auf einen identischen Term reduziert werden können - \end{itemize*} - \item Der Term Y ist übrigens nicht der einzige Kombinator, der Fixpunkte zu Lambda-Termen konstruiert - \begin{itemize*} - \item A. Turing: $\Theta \equiv (\lambda xy.y(xxy)) (\lambda xy.y(xxy))$ - \end{itemize*} + \item Lösungsansatz: $W\equiv\lambda x.(\lambda\color{blue}x\color{black}y.\color{blue}x\color{black}y)\color{red}(xx) \color{black} \equiv\lambda x.\lambda y.(x x)y \equiv \lambda xy.(xx)y$ + \item Damit ist der gesuchte Fixpunkt $X\equiv((\lambda xy.(xx)y)(\lambda xy.(xx)y))$ + \item Nachrechnen: + \begin{description*} + \item[ \space ] $(\lambda xy.xy) ((\lambda xy.(xx)y) (\lambda xy.(xx)y))$ + \item[$\equiv$] $(\lambda \color{blue}x\color{black}.\lambda y. \color{blue} x \color{black} y) \color{red} ((\lambda xy.(xx)y) (\lambda xy.(xx)y)$ \color{black} + \item[$\equiv$] $\lambda \color{blue}y \color{black}.((\lambda xy.(xx)y)) (\lambda xy.(xx)y) \color{red}y\color{black}$ + \item[$\equiv$] $(\lambda xy.(xx)y)(\lambda xy.(xx)y)$ + \item[$\equiv$] X + \end{description*} \end{itemize*} - - \subsubsection{Rekursion im Lambda-Kalkül} + \item Bemerkung: Der so für die Identitätsfunktion $\lambda x.x$ konstruierte Fixpunkt ist übrigens $(\lambda x.xx)(\lambda x.xx)$, er spielt die besondere Rolle des Standardterms $\bot$ für nicht-terminierende Ausführungen +\end{itemize*} + +\subsubsection{Der Fixpunkt-Kombinator} +\colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Im Ergebnis unserer Diskussion des Fixpunktsatzes definieren wir den Fixpunkt-Kombinator wie folgt: $$Y \equiv \lambda f.(\lambda x.f(xx)) (\lambda x.f(xx))$$ + \end{minipage} +} +\begin{itemize*} + \item Dieser Kombinator spielt eine wichtige Rolle bei der Definition rekursiver Funktionen im Lambda-Kalkül, wie wir im folgenden sehen werden + \item Für jeden Lambda-Term M gilt: $Y M = M (Y M)$ \begin{itemize*} - \item Die bisher definierten Funktionen waren alle nicht-rekursiv - \item Viele Funktionen kann man aber nur unter Zuhilfenahme von Rekursion (bzw. Iteration) beschreiben - \item In üblichen Programmiersprachen werden rekursive Funktionsdefinitionen durch die Verwendung von Namen für Funktionen möglich - man verwendet hierbei einfach den Namen der gerade zu definierenden Funktion im Rumpf der Definition: - \begin{lstlisting} + \item Beweisidee: zeige, dass beide Terme auf einen identischen Term reduziert werden können + \end{itemize*} + \item Der Term Y ist übrigens nicht der einzige Kombinator, der Fixpunkte zu Lambda-Termen konstruiert + \begin{itemize*} + \item A. Turing: $\Theta \equiv (\lambda xy.y(xxy)) (\lambda xy.y(xxy))$ + \end{itemize*} +\end{itemize*} + +\subsubsection{Rekursion im Lambda-Kalkül} +\begin{itemize*} + \item Die bisher definierten Funktionen waren alle nicht-rekursiv + \item Viele Funktionen kann man aber nur unter Zuhilfenahme von Rekursion (bzw. Iteration) beschreiben + \item In üblichen Programmiersprachen werden rekursive Funktionsdefinitionen durch die Verwendung von Namen für Funktionen möglich - man verwendet hierbei einfach den Namen der gerade zu definierenden Funktion im Rumpf der Definition: + \begin{lstlisting} \item fun fak(i) -> if (i = 0) then 1 else i * fak(i-1). \end{lstlisting} - \item Im Lambda-Kalkül gibt es jedoch keine Namen für Funktionen: - \begin{itemize*} - \item Daher stellt man eine rekursive Funktion f mittels einer Funktion G dar, die einen zusätzlichen Parameter g hat, an den man dann G selber bildet - \item Schaut kompliziert aus, ist es auch (Q-Q) - \item Warum so kompliziert? Damit die Definition von G im eigenen Rumpf verfügbar ist - \end{itemize*} - \end{itemize*} - - \subsubsection{Rekursive Funktionen sind Fixpunkte} + \item Im Lambda-Kalkül gibt es jedoch keine Namen für Funktionen: \begin{itemize*} - \item Rekursive Funktion von g - \begin{itemize*} - \item $g = \lambda n...g...n...$ Rumpf verwendet g - \end{itemize*} - \item Daraus gewinnt man das Funktional - \begin{itemize*} - \item $G = \lambda g.\lambda n...g...n...$ - \end{itemize*} - \item Falls G einen Fixpunkt g* hat, d.h. $G(g*) = g*$, so - \begin{itemize*} - \item $g* = G(g*) = \lambda n...g*...n$ - \end{itemize*} - \item Vergleiche: $g = \lambda n...g...n...$ + \item Daher stellt man eine rekursive Funktion f mittels einer Funktion G dar, die einen zusätzlichen Parameter g hat, an den man dann G selber bildet + \item Schaut kompliziert aus, ist es auch (Q-Q) + \item Warum so kompliziert? Damit die Definition von G im eigenen Rumpf verfügbar ist \end{itemize*} +\end{itemize*} + +\subsubsection{Rekursive Funktionen sind Fixpunkte} +\begin{itemize*} + \item Rekursive Funktion von g + \begin{itemize*} + \item $g = \lambda n...g...n...$ Rumpf verwendet g + \end{itemize*} + \item Daraus gewinnt man das Funktional + \begin{itemize*} + \item $G = \lambda g.\lambda n...g...n...$ + \end{itemize*} + \item Falls G einen Fixpunkt g* hat, d.h. $G(g*) = g*$, so + \begin{itemize*} + \item $g* = G(g*) = \lambda n...g*...n$ + \end{itemize*} + \item Vergleiche: $g = \lambda n...g...n...$ +\end{itemize*} +\begin{center} + Rekursive Definition $\Leftrightarrow$ Fixpunkt des Funktionals +\end{center} +\begin{itemize*} + \item Beispiel: Fakultät + \begin{itemize*} + \item $g = \lambda n. $ if isZero n then $c_1$ else (times n g(pred n)) - rekursiv + \item $G = \lambda g.\lambda n.$ if isZero n then $c_1$ else (times n g(pred n)) - funktional + \end{itemize*} +\end{itemize*} + +\subsubsection{Der Fixpunktkombinator dient als Rekursionsoperator} +Wir berechnen den gesuchten Fixpunkt des Funktionals G mit dem Fixpunktkombinator, der somit als Rekusrsionsoperator dient: + +\color{blue} Rekursionsoperator \color{black} +$$Y = \lambda f.(\lambda x.f(xx))(\lambda x.f(xx))$$ +$$Y f = (\lambda \color{blue}f\color{black}.(\lambda x.\color{blue}f\color{black}(xx))(\lambda x.\color{blue}f\color{black}(xx))) \color{red}f\color{black}$$ +$$\Rightarrow (\lambda \color{blue}x\color{black}.f(\color{blue}xx\color{black})) \color{red}(\lambda x.f(xx))\color{black}$$ +$$\Rightarrow f((\lambda x.f(xx)) (\lambda x.f(xx)) \Leftarrow f(Yf) ())$$ +\color{black} +\subitem also \space\space\space \space $f(Yf) \stackrel{\beta}{=} Yf$ \\ +d.h. \space\space\space Yf ist Fixpunkt von f + +\paragraph{Beispiel: Fakultät im Lambda-Kalkül} +\includegraphics[width=.4\linewidth]{Assets/Programmierparadigmen-Lambda_Abstraktion} + +\subsection{Ausdrucksstärke des Lambdakalküls} +\begin{itemize*} + \item Im Folgenden wollen wir zeigen, dass der Lambda-Kalkül genau die rekursiven Funktionen beschreibt + \item Eine numerische Funktion ist eine Abbildung $f:\mathbb{N}^k \rightarrow \mathbb{N}$ mit $k\in\mathbb{N} \cap \{0\}$ + \item Wir definieren hierzu: + \begin{itemize*} + \item Anfangsfunktionen: + \begin{itemize*} + \item Projektion: $U_i^k (n_1,n_2,...,n_k) = n_i$ für $1<=i<=k$ + \item Nullfunktion: $Z(n) = 0$ + \item Nachfolger: $S(n) = n+1$ + \end{itemize*} + \item Minimalisierung: + \begin{itemize*} + \item Für eine Relation $P(m)$ bezeichne $\mu m[P(m)]$ die kleinste Zahl m sodass P(m) gilt. + \end{itemize*} + \end{itemize*} + \item Bemerkung: im Folgenden notieren wir $n_1,n_2,...,n_k$ kurz als $\overline{n_k}$ + \item Eine numerische Funktion ist Lambda-definierbar, wenn es einen Kombinator M gibt, sodass $M\overline{n_k} = f(\overline{n_k})$ +\end{itemize*} + +\begin{itemize*} + \item Im folgenden sei C eine Klasse von numerischen Funktionen, und es gelte $g,h,h_1,h_2,...,h_m \in C$ + \item Wir definieren nun die folgenden Eigenschaften: + \begin{itemize*} + \item C ist \color{blue} abgeschlossen unter Komposition\color{black}, wenn für jede Funktion f, die über $f(\overline{n_k}):= g(h_1(\overline{n_k}),...,h_m(\overline{n_k}))$ definiert ist, gilt $f \in C$ + \item C ist \color{blue} abgeschlossen unter primitiver Rekursion\color{black}, wenn für jede Funktion f, die über + $$f(0,\overline{n_k}) = g(\overline{n_k})$$ + $$f(j+1, \overline{n_k}) = h(f(j,\overline{n_k}),j,\overline{n_k})$$ + definiert ist, gilt: $f \in C$ + \item C ist \color{blue} abgeschlossen unter unbeschränkter Minimalisierung \color{black}, wenn für jede Funktion f, die über $f(\overline{n_k})=\mu m[g(\overline{n_k},m)= 0]$ definiert ist (wobei für alle $\overline{n_k}$ ein m existiere, sodass $g(\overline{n_k},m) = 0$ ist), gilt $f \in C$ + \end{itemize*} +\end{itemize*} + +\colorbox{lightgray}{\begin{minipage}[h]{1.0\linewidth} + Definition: \\ + Die Klasse der rekursiven Funktionen ist die kleinste Klasse numerischer Funktionen, die alle oben genannten Anfangsfunktionen enthält und abgeschlossen ist unter Komposition, primitiver Rekursion und unbeschränkter Minimalisierung + \end{minipage} +} + +\begin{itemize*} + \item \color{blue} Lemma 1: Die Anfangsfunktionen sind Lambda-definierbar \color{black} + \item Beweis: + \begin{itemize*} + \item $U_i^k = \lambda x_1 x_2 ... x_k.x_i$ + \item $S = \lambda n.\lambda s. \lambda z.s(nsz)$ (siehe succ bei Churchzahlen) + \item $Z = \lambda fx.x$ (siehe $c_0$ bei Churchzahlen) + \end{itemize*} +\end{itemize*} + +\begin{itemize*} + \item \color{blue} Lemma 2: Die Lambda-definierbaren Funktionen sind abgeschlossen unter primitiver Rekursion \color{black} + \item Beweis: Sei f definiert über \begin{center} - Rekursive Definition $\Leftrightarrow$ Fixpunkt des Funktionals + $$f(0,\overline{n_k}) = g(\overline{n_k})$$ + $$f(j+1, \overline{n_k}) = h(f(j, \overline{n_k}),j,\overline{n_k})$$ \end{center} + und seien g und h Funktionen (die per Induktionsvoraussetzung) durch die Lambda-terme G und H berechnet werden \begin{itemize*} - \item Beispiel: Fakultät - \begin{itemize*} - \item $g = \lambda n. $ if isZero n then $c_1$ else (times n g(pred n)) - rekursiv - \item $G = \lambda g.\lambda n.$ if isZero n then $c_1$ else (times n g(pred n)) - funktional - \end{itemize*} + \item Intuitiv kann f berechnet werden, indem man überprüft ob j = 0 ist, und wenn ja $g(\overline{n_k})$, ansonsten $h(f(j, \overline{n_k}),j,\overline{n_k})$ + \item Ein Term M hierfür existiert laut Fixpunktsatz und es gilt: + $M \equiv Y (\lambda f\:x\: \overline{y_k}.if(isZero \: x)(G\:\overline{y_k})(H(f(pred\: x)\overline{y_k})(pred \: x)\overline{y_k}))$ \end{itemize*} - - \subsubsection{Der Fixpunktkombinator dient als Rekursionsoperator} - Wir berechnen den gesuchten Fixpunkt des Funktionals G mit dem Fixpunktkombinator, der somit als Rekusrsionsoperator dient: - - \color{blue} Rekursionsoperator \color{black} - $$Y = \lambda f.(\lambda x.f(xx))(\lambda x.f(xx))$$ - $$Y f = (\lambda \color{blue}f\color{black}.(\lambda x.\color{blue}f\color{black}(xx))(\lambda x.\color{blue}f\color{black}(xx))) \color{red}f\color{black}$$ - $$\Rightarrow (\lambda \color{blue}x\color{black}.f(\color{blue}xx\color{black})) \color{red}(\lambda x.f(xx))\color{black}$$ - $$\Rightarrow f((\lambda x.f(xx)) (\lambda x.f(xx)) \Leftarrow f(Yf) ())$$ +\end{itemize*} + +\begin{itemize*} + \item \color{blue} Lemma 3: Die Lambda-definierbaren Funktionen sind abgeschlossen unter unbeschränkter Minimalisierung \color{black} + \item Beweis: + \begin{itemize*} + \item Sei f über $f(\overline{n_k}) = \mu m[g(\overline{n_k},m) = 0]$ definiert, wobei g (per Induktionsvoraussetzung) durch den Lambda-Term G berechnet wird + \item Intuitiv kann man f berechnen, indem man bei 0 beginnend für m überprüft, ob $g(\overline{n_k},m) = 0$ ist, und wenn ja m ausgibt, ansonsten die Überprüfung mit $m+1$ fortsetzt + \item Ein Term für eine solche Funktion kann laut Fixpunktsatz konstruiert werden und man erhält mit Anwendung des Fixpunktkombinators zunächst: $$N \equiv Y (\lambda f \: \overline{x_k} \: y. if(isZero \: (G \: \overline{x_k} \: y))y(f\:\overline{x_k}\:(succ \: y)))$$ + \item Nun definiert man die Funktion f durch den folgenden Term M: $$M \equiv \lambda \overline{x_k}.N \: \overline{x_k} \: c_0$$ + \end{itemize*} +\end{itemize*} + +\begin{center} + \includegraphics[width=.4\linewidth]{Assets/Programmierparadigmen-kodierung-natürlicher-zahlen} +\end{center} +\begin{itemize*} + \item Aus den Lemmata 1 bis 3 folgt nun der Satz:\\ + \colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Alle rekursiven Funktionen sind Lambda-definierbar + \end{minipage}} +\end{itemize*} + + +\subsection{Berechnungsreihenfolgen und Konfluenz} +\subsubsection{Noch einmal Auswertungsstrategien} +\begin{itemize*} + \item Bei unserer initialen Betrachtung der Auswertungsstrategien haben wir die volle $\beta$-Rekursion und die Normalreihenfolge kennengelernt + \item Nun wollen wir unsere Betrachtungen hierzu noch einmal vertiefen und definieren zunächst: + \begin{itemize*} + \item Ein Redex wird als \color{blue} "äußerst" (outermost) \color{black} bezeichnet, wenn er nicht Teil eines anderen Redex ist. + \item Ein Redex wird als \color{blue} "innerst" (innermost) \color{black} bezeichnet, wenn er keinen eigenständigen Redex beinhaltet + \end{itemize*} + \item Mit diesen Begriffen können im folgenden die gebräuchlichsten Auswertungsstrategien formuliert werden + \begin{itemize*} + \item \color{blue} Normal Order: \color{black} Evaluiere Argumente so oft, wie sie verwendet werden + \item \color{blue} Applicative Order: \color{black} Evaluiere Argumente einmal + \item \color{blue} Lazy Evaluation: \color{black} Evaluiere Argumente höchstens einmal + \end{itemize*} + \item Eine zentrale Kernfrage: \color{blue} Welche Auswertungsstrategie führt (möglichst schnell) zu einem nicht weiter reduzierbaren Term? \color{black} - \subitem also \space\space\space \space $f(Yf) \stackrel{\beta}{=} Yf$ \\ - d.h. \space\space\space Yf ist Fixpunkt von f - - \paragraph{Beispiel: Fakultät im Lambda-Kalkül} - \includegraphics[width=.4\linewidth]{Assets/Programmierparadigmen-Lambda_Abstraktion} - - \subsection{Ausdrucksstärke des Lambdakalküls} \begin{itemize*} - \item Im Folgenden wollen wir zeigen, dass der Lambda-Kalkül genau die rekursiven Funktionen beschreibt - \item Eine numerische Funktion ist eine Abbildung $f:\mathbb{N}^k \rightarrow \mathbb{N}$ mit $k\in\mathbb{N} \cap \{0\}$ - \item Wir definieren hierzu: - \begin{itemize*} - \item Anfangsfunktionen: - \begin{itemize*} - \item Projektion: $U_i^k (n_1,n_2,...,n_k) = n_i$ für $1<=i<=k$ - \item Nullfunktion: $Z(n) = 0$ - \item Nachfolger: $S(n) = n+1$ - \end{itemize*} - \item Minimalisierung: - \begin{itemize*} - \item Für eine Relation $P(m)$ bezeichne $\mu m[P(m)]$ die kleinste Zahl m sodass P(m) gilt. - \end{itemize*} - \end{itemize*} - \item Bemerkung: im Folgenden notieren wir $n_1,n_2,...,n_k$ kurz als $\overline{n_k}$ - \item Eine numerische Funktion ist Lambda-definierbar, wenn es einen Kombinator M gibt, sodass $M\overline{n_k} = f(\overline{n_k})$ + \item Bei unserer beispielhaften Berechnung des Terms Fak $c_2$ haben wir nach der initialen Anwendung des Fixpunktkombinators zunächst den Term isZero $c_2$ reduziert. + \item Ebenso hätten wird den weiter innen stehenden Fixpunktkombinator zuerst erneut anwenden können(bei voller $\beta$-Reduktion kann jeder Term jederzeit reduziert werden). + \item Auf diese Weise hätten wir unendlich oft vorgehen, damit einen immer länger werdenden Term ableiten können und somit nicht das gewünschte Resultat $c_2$ berechnet. \end{itemize*} - - \begin{itemize*} - \item Im folgenden sei C eine Klasse von numerischen Funktionen, und es gelte $g,h,h_1,h_2,...,h_m \in C$ - \item Wir definieren nun die folgenden Eigenschaften: - \begin{itemize*} - \item C ist \color{blue} abgeschlossen unter Komposition\color{black}, wenn für jede Funktion f, die über $f(\overline{n_k}):= g(h_1(\overline{n_k}),...,h_m(\overline{n_k}))$ definiert ist, gilt $f \in C$ - \item C ist \color{blue} abgeschlossen unter primitiver Rekursion\color{black}, wenn für jede Funktion f, die über - $$f(0,\overline{n_k}) = g(\overline{n_k})$$ - $$f(j+1, \overline{n_k}) = h(f(j,\overline{n_k}),j,\overline{n_k})$$ - definiert ist, gilt: $f \in C$ - \item C ist \color{blue} abgeschlossen unter unbeschränkter Minimalisierung \color{black}, wenn für jede Funktion f, die über $f(\overline{n_k})=\mu m[g(\overline{n_k},m)= 0]$ definiert ist (wobei für alle $\overline{n_k}$ ein m existiere, sodass $g(\overline{n_k},m) = 0$ ist), gilt $f \in C$ - \end{itemize*} - \end{itemize*} - + \item Eine weitere Kernfrage: Angenommen mehrere unterschiedliche Reduktionsreihenfolgen führen zu einem nicht weiter zu reduzierenden Ergebnis - \color{blue} führen all diese Reihenfolgen zum gleichen Ergebnis? \color{black} + \item Wir definieren zuerst einen zentralen begriff in diesem Zusammenhang: \colorbox{lightgray}{\begin{minipage}[h]{1.0\linewidth} - Definition: \\ - Die Klasse der rekursiven Funktionen ist die kleinste Klasse numerischer Funktionen, die alle oben genannten Anfangsfunktionen enthält und abgeschlossen ist unter Komposition, primitiver Rekursion und unbeschränkter Minimalisierung - \end{minipage} - } - + Ein Transitiosnsystem $(D,\rightarrow*)$ heißt genau dann konfluent, wenn für alle $t,t_1,t_2 \in D$ gilt: wenn $ t \rightarrow* t_1$ und $t \rightarrow* t_2$, dann gibt es ein $t' \in D$ mit $t_1 \rightarrow* t'$ und $t_2 \rightarrow* t'$ + \end{minipage}} + \item Wenn der Lambda-Kalkül konfluent ist, kann hieraus gefolgert werden, dass unterschiedliche Reduktionsreihenfolgen, die zu einer nicht mehr weiter zu reduzierenden Form führen, somit auf den gleichen Term führen müssen. + \item Achtung: hieraus kann nicht gefolgert werden, dass alle Reduktionsreihenfolgen auf den gleichen Term führen, da dies ja nur für "terminierende" Reduktionsreihenfolgen gilt! +\end{itemize*} + +\subsubsection{Church-Rosser-Eigenschaft} +\color{blue} Satz (Church-Rosser) \\ +Der untypisierte $\lambda$-Kalkül ist konfluent: Wenn $t \stackrel{*}{\Rightarrow} t_1$ und $t \stackrel{*}{\Rightarrow} t_2$, dann gibt es ein t' mit $t_1 \stackrel{*}{\Rightarrow} t'$ und $t_2 \stackrel{*}{\Rightarrow} t'$ +\color{black} + +\begin{center} + \includegraphics[width=0.25\linewidth]{Assets/Programmierparadigmen-diamant-eigenschaft.png} + \includegraphics[width=0.25\linewidth]{Assets/Programmierparadigmen-diamant-beispiel} +\end{center} + +Beweisidee: Definiere $\stackrel{\rightarrow}{\rightarrow}$ als "parallele" $\beta$-Reduktion. +\begin{itemize*} + \item Es gilt: $\Rightarrow \subseteq \stackrel{\rightarrow}{\rightarrow} \subseteq \stackrel{*}{\Rightarrow}$ + \item Zeige Diamant Eigenschaft für $\stackrel{\rightarrow}{\rightarrow}$ +\end{itemize*} + +\subsubsection{Eindeutigkeit der Normalform} +\color{blue} Korollar (Eindeutigkeit der Normalform) \newline +Die Normalform eines $\lambda$-Terms ist - sofern sie existiert - eindeutig. \color{black} +\newline +\newline +Beweis: +\begin{itemize*} + \item $t_1$ und $t_2$ Normalformen von t, d.h. $t \stackrel{*}{\Rightarrow} t_1 \nRightarrow$ und $t \stackrel{*}{\Rightarrow} t_2 \nRightarrow$ + \item Nach Chruch-Rosser gibt es t' mit $t_1 \stackrel{*}{\Rightarrow} t'$ und $t_2 \stackrel{*}{\Rightarrow} t'$ + \item Nach Annahme $t_1 \nRightarrow$ und $t_2 \nRightarrow$, also $t_1 = t' = t_2$ +\end{itemize*}\ \newline +\color{blue} +Bei terminierenden $\beta$-Reduktionen ist irrelevant, welchen Redex man zuerst reduziert!\color{black} +\subsection{Auswertung von Parametern in Programmiersprachen} +\subsubsection{Behandlung von Parametern in Programmiersprachen} +\begin{itemize*} + \item Die Art und Weise, wie in einer Programmiersprache Parametter übergeben - d.h. wie die Reihenfolge und die Zeitpunkte ihrer Auswertung gehandhabt - werden, hat Einfluss auf wichtige Eigenschaften der Sprache: \begin{itemize*} - \item \color{blue} Lemma 1: Die Anfangsfunktionen sind Lambda-definierbar \color{black} - \item Beweis: - \begin{itemize*} - \item $U_i^k = \lambda x_1 x_2 ... x_k.x_i$ - \item $S = \lambda n.\lambda s. \lambda z.s(nsz)$ (siehe succ bei Churchzahlen) - \item $Z = \lambda fx.x$ (siehe $c_0$ bei Churchzahlen) - \end{itemize*} + \item Effizienz der Berechnungen + \item Termininerungsverhalten + \item Ausdruckskraft \end{itemize*} - + \item Hierbei ist es insbesondere von Interesse, wie Parameter gehandhabt werden, deren Werte undefiniert sind (z.B. 1/0)\newline + \colorbox{lightgray}{ + \begin{minipage}[h]{1.0\linewidth} + Wir definieren zunächst den zentralen begriff "strikt": \newline Eine n-stellige Funktion heißt strikt im k-ten Argument $(1<=k<=n)$, wenn gilt: $f(x_1,x_2,...,x_{k-1},\bot,x_{k+1},...,x_n)=\bot$ + \end{minipage}} + \item Ein undefiniertes Argument führt hier zu einem undefinierten Resultat + \item Grundsätzlich kann man die Auswertungsstrategien von Programmiersprachen in strikte und nicht-strikte Strategien einteilen; sehr gebräuchlich sind dabei insbesondere: \begin{itemize*} - \item \color{blue} Lemma 2: Die Lambda-definierbaren Funktionen sind abgeschlossen unter primitiver Rekursion \color{black} - \item Beweis: Sei f definiert über - \begin{center} - $$f(0,\overline{n_k}) = g(\overline{n_k})$$ - $$f(j+1, \overline{n_k}) = h(f(j, \overline{n_k}),j,\overline{n_k})$$ - \end{center} - und seien g und h Funktionen (die per Induktionsvoraussetzung) durch die Lambda-terme G und H berechnet werden - \begin{itemize*} - \item Intuitiv kann f berechnet werden, indem man überprüft ob j = 0 ist, und wenn ja $g(\overline{n_k})$, ansonsten $h(f(j, \overline{n_k}),j,\overline{n_k})$ - \item Ein Term M hierfür existiert laut Fixpunktsatz und es gilt: - $M \equiv Y (\lambda f\:x\: \overline{y_k}.if(isZero \: x)(G\:\overline{y_k})(H(f(pred\: x)\overline{y_k})(pred \: x)\overline{y_k}))$ - \end{itemize*} + \item Call by Value: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden vor der Übergabe an die Funktion vollständig ausgewertet + \item Call by Name: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden nicht bei Übergabe, sondern erst dann ausgewertet, wenn sie in der aufgerufenen Funktion tatsächlich benötigt werden \end{itemize*} - + \item Beide Varianten haben spezifische Vor- und Nachteile: \begin{itemize*} - \item \color{blue} Lemma 3: Die Lambda-definierbaren Funktionen sind abgeschlossen unter unbeschränkter Minimalisierung \color{black} - \item Beweis: - \begin{itemize*} - \item Sei f über $f(\overline{n_k}) = \mu m[g(\overline{n_k},m) = 0]$ definiert, wobei g (per Induktionsvoraussetzung) durch den Lambda-Term G berechnet wird - \item Intuitiv kann man f berechnen, indem man bei 0 beginnend für m überprüft, ob $g(\overline{n_k},m) = 0$ ist, und wenn ja m ausgibt, ansonsten die Überprüfung mit $m+1$ fortsetzt - \item Ein Term für eine solche Funktion kann laut Fixpunktsatz konstruiert werden und man erhält mit Anwendung des Fixpunktkombinators zunächst: $$N \equiv Y (\lambda f \: \overline{x_k} \: y. if(isZero \: (G \: \overline{x_k} \: y))y(f\:\overline{x_k}\:(succ \: y)))$$ - \item Nun definiert man die Funktion f durch den folgenden Term M: $$M \equiv \lambda \overline{x_k}.N \: \overline{x_k} \: c_0$$ - \end{itemize*} + \item Call by Value: weniger Berechnungsaufwand, wenn ein Parameter mehr als einmal im Funktionsrumpf vorkommt; weniger Speicheraufwand bei der Übergabe + \item Call by Name: weniger Berechnungsaufwand, wenn ein Argument nicht zum Ergebnis beiträgt; höherer Aufwand bei Übergabe + \end{itemize*} + \item Die Programmiersprache Erlang realisiert grundsätzlich eine strikte Handhabung von Parametern, da sie die Strategie Call by Value verwendet + \item Allerdings wird bei der Definition einer Funktion der resultierende Wert erst dann berechnet, wenn die Funktion ausgewertet wird + \begin{itemize*} + \item Das erlaubt über den Umweg zusätzlicher Funktionsdefinitionen auch die Realisierung einer nicht-strikten Auswertungsstrategie - ermöglicht Nachbildung der sogenannten Lazy-Evaluation + \item hierbei wird ein nicht-strikt zu evaluierendes Argument als Resultat einer anonymen nullstelligen Funktion (ohne Parameter) "verpackt" + \item Im Rumpf der eigentlichen Funktion wird diese Funktion dann ausgewertet (= aufgerufen), wenn feststeht, dass dieses Argument für die Berechnung des Ergebnisses benötigt wird + \item Andere funktionale Sprachen wie Haskell oder Gofer verwenden Call by Name und realisieren damit grundsätzlich Lazy-Evaluation \end{itemize*} - \begin{center} - \includegraphics[width=.4\linewidth]{Assets/Programmierparadigmen-kodierung-natürlicher-zahlen} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-66} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-67} \end{center} + \item Erläuterungen: \begin{itemize*} - \item Aus den Lemmata 1 bis 3 folgt nun der Satz:\\ - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Alle rekursiven Funktionen sind Lambda-definierbar - \end{minipage}} - \end{itemize*} - - - \subsection{Berechnungsreihenfolgen und Konfluenz} - \subsubsection{Noch einmal Auswertungsstrategien} - \begin{itemize*} - \item Bei unserer initialen Betrachtung der Auswertungsstrategien haben wir die volle $\beta$-Rekursion und die Normalreihenfolge kennengelernt - \item Nun wollen wir unsere Betrachtungen hierzu noch einmal vertiefen und definieren zunächst: - \begin{itemize*} - \item Ein Redex wird als \color{blue} "äußerst" (outermost) \color{black} bezeichnet, wenn er nicht Teil eines anderen Redex ist. - \item Ein Redex wird als \color{blue} "innerst" (innermost) \color{black} bezeichnet, wenn er keinen eigenständigen Redex beinhaltet - \end{itemize*} - \item Mit diesen Begriffen können im folgenden die gebräuchlichsten Auswertungsstrategien formuliert werden - \begin{itemize*} - \item \color{blue} Normal Order: \color{black} Evaluiere Argumente so oft, wie sie verwendet werden - \item \color{blue} Applicative Order: \color{black} Evaluiere Argumente einmal - \item \color{blue} Lazy Evaluation: \color{black} Evaluiere Argumente höchstens einmal - \end{itemize*} - \item Eine zentrale Kernfrage: \color{blue} Welche Auswertungsstrategie führt (möglichst schnell) zu einem nicht weiter reduzierbaren Term? - \color{black} - \begin{itemize*} - \item Bei unserer beispielhaften Berechnung des Terms Fak $c_2$ haben wir nach der initialen Anwendung des Fixpunktkombinators zunächst den Term isZero $c_2$ reduziert. - \item Ebenso hätten wird den weiter innen stehenden Fixpunktkombinator zuerst erneut anwenden können(bei voller $\beta$-Reduktion kann jeder Term jederzeit reduziert werden). - \item Auf diese Weise hätten wir unendlich oft vorgehen, damit einen immer länger werdenden Term ableiten können und somit nicht das gewünschte Resultat $c_2$ berechnet. - \end{itemize*} - \item Eine weitere Kernfrage: Angenommen mehrere unterschiedliche Reduktionsreihenfolgen führen zu einem nicht weiter zu reduzierenden Ergebnis - \color{blue} führen all diese Reihenfolgen zum gleichen Ergebnis? \color{black} - \item Wir definieren zuerst einen zentralen begriff in diesem Zusammenhang: - \colorbox{lightgray}{\begin{minipage}[h]{1.0\linewidth} - Ein Transitiosnsystem $(D,\rightarrow*)$ heißt genau dann konfluent, wenn für alle $t,t_1,t_2 \in D$ gilt: wenn $ t \rightarrow* t_1$ und $t \rightarrow* t_2$, dann gibt es ein $t' \in D$ mit $t_1 \rightarrow* t'$ und $t_2 \rightarrow* t'$ - \end{minipage}} - \item Wenn der Lambda-Kalkül konfluent ist, kann hieraus gefolgert werden, dass unterschiedliche Reduktionsreihenfolgen, die zu einer nicht mehr weiter zu reduzierenden Form führen, somit auf den gleichen Term führen müssen. - \item Achtung: hieraus kann nicht gefolgert werden, dass alle Reduktionsreihenfolgen auf den gleichen Term führen, da dies ja nur für "terminierende" Reduktionsreihenfolgen gilt! - \end{itemize*} - - \subsubsection{Church-Rosser-Eigenschaft} - \color{blue} Satz (Church-Rosser) \\ - Der untypisierte $\lambda$-Kalkül ist konfluent: Wenn $t \stackrel{*}{\Rightarrow} t_1$ und $t \stackrel{*}{\Rightarrow} t_2$, dann gibt es ein t' mit $t_1 \stackrel{*}{\Rightarrow} t'$ und $t_2 \stackrel{*}{\Rightarrow} t'$ - \color{black} - - \begin{center} - \includegraphics[width=0.25\linewidth]{Assets/Programmierparadigmen-diamant-eigenschaft.png} - \includegraphics[width=0.25\linewidth]{Assets/Programmierparadigmen-010.png} - \end{center} - - Beweisidee: Definiere $\stackrel{\rightarrow}{\rightarrow}$ als "parallele" $\beta$-Reduktion. - \begin{itemize*} - \item Es gilt: $\Rightarrow \subseteq \stackrel{\rightarrow}{\rightarrow} \subseteq \stackrel{*}{\Rightarrow}$ - \item Zeige Diamant Eigenschaft für $\stackrel{\rightarrow}{\rightarrow}$ - \end{itemize*} - - \subsubsection{Eindeutigkeit der Normalform} - \color{blue} Korollar (Eindeutigkeit der Normalform) \newline - Die Normalform eines $\lambda$-Terms ist - sofern sie existiert - eindeutig. \color{black} - \newline - \newline - Beweis: - \begin{itemize*} - \item $t_1$ und $t_2$ Normalformen von t, d.h. $t \stackrel{*}{\Rightarrow} t_1 \nRightarrow$ und $t \stackrel{*}{\Rightarrow} t_2 \nRightarrow$ - \item Nach Chruch-Rosser gibt es t' mit $t_1 \stackrel{*}{\Rightarrow} t'$ und $t_2 \stackrel{*}{\Rightarrow} t'$ - \item Nach Annahme $t_1 \nRightarrow$ und $t_2 \nRightarrow$, also $t_1 = t' = t_2$ - \end{itemize*}\ \newline - \color{blue} - Bei terminierenden $\beta$-Reduktionen ist irrelevant, welchen Redex man zuerst reduziert!\color{black} - \subsection{Auswertung von Parametern in Programmiersprachen} - \subsubsection{Behandlung von Parametern in Programmiersprachen} - \begin{itemize*} - \item Die Art und Weise, wie in einer Programmiersprache Parametter übergeben - d.h. wie die Reihenfolge und die Zeitpunkte ihrer Auswertung gehandhabt - werden, hat Einfluss auf wichtige Eigenschaften der Sprache: - \begin{itemize*} - \item Effizienz der Berechnungen - \item Termininerungsverhalten - \item Ausdruckskraft - \end{itemize*} - \item Hierbei ist es insbesondere von Interesse, wie Parameter gehandhabt werden, deren Werte undefiniert sind (z.B. 1/0)\newline - \colorbox{lightgray}{ - \begin{minipage}[h]{1.0\linewidth} - Wir definieren zunächst den zentralen begriff "strikt": \newline Eine n-stellige Funktion heißt strikt im k-ten Argument $(1<=k<=n)$, wenn gilt: $f(x_1,x_2,...,x_{k-1},\bot,x_{k+1},...,x_n)=\bot$ - \end{minipage}} - \item Ein undefiniertes Argument führt hier zu einem undefinierten Resultat - \item Grundsätzlich kann man die Auswertungsstrategien von Programmiersprachen in strikte und nicht-strikte Strategien einteilen; sehr gebräuchlich sind dabei insbesondere: - \begin{itemize*} - \item Call by Value: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden vor der Übergabe an die Funktion vollständig ausgewertet - \item Call by Name: Ausdrücke, die Parameter bei einem Funktionsaufruf beschreiben, werden nicht bei Übergabe, sondern erst dann ausgewertet, wenn sie in der aufgerufenen Funktion tatsächlich benötigt werden - \end{itemize*} - \item Beide Varianten haben spezifische Vor- und Nachteile: - \begin{itemize*} - \item Call by Value: weniger Berechnungsaufwand, wenn ein Parameter mehr als einmal im Funktionsrumpf vorkommt; weniger Speicheraufwand bei der Übergabe - \item Call by Name: weniger Berechnungsaufwand, wenn ein Argument nicht zum Ergebnis beiträgt; höherer Aufwand bei Übergabe - \end{itemize*} - \item Die Programmiersprache Erlang realisiert grundsätzlich eine strikte Handhabung von Parametern, da sie die Strategie Call by Value verwendet - \item Allerdings wird bei der Definition einer Funktion der resultierende Wert erst dann berechnet, wenn die Funktion ausgewertet wird - \begin{itemize*} - \item Das erlaubt über den Umweg zusätzlicher Funktionsdefinitionen auch die Realisierung einer nicht-strikten Auswertungsstrategie - ermöglicht Nachbildung der sogenannten Lazy-Evaluation - \item hierbei wird ein nicht-strikt zu evaluierendes Argument als Resultat einer anonymen nullstelligen Funktion (ohne Parameter) "verpackt" - \item Im Rumpf der eigentlichen Funktion wird diese Funktion dann ausgewertet (= aufgerufen), wenn feststeht, dass dieses Argument für die Berechnung des Ergebnisses benötigt wird - \item Andere funktionale Sprachen wie Haskell oder Gofer verwenden Call by Name und realisieren damit grundsätzlich Lazy-Evaluation - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-012.png} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-013.png} - \end{center} - \item Erläuterungen: - \begin{itemize*} - \item Im zweiten Beispiel wird der Rückgabewert der übergebenen Funktionne nur ausgewertet, wenn sie im Rumpf der auszuführenden Funktion aufgerufen werden - \item Innerhalb von Erlang-Modulen kann man sich mit Hilfe einer Macro-Definition Schreibarbeit sparen: - \begin{lstlisting}[language=erlang] + \item Im zweiten Beispiel wird der Rückgabewert der übergebenen Funktionne nur ausgewertet, wenn sie im Rumpf der auszuführenden Funktion aufgerufen werden + \item Innerhalb von Erlang-Modulen kann man sich mit Hilfe einer Macro-Definition Schreibarbeit sparen: + \begin{lstlisting}[language=erlang] -define(DELAY(E), fun() -> E end). check() -> test2(true, ?DELAY(3), ?DELAY(4/0)). \end{lstlisting} - \end{itemize*} - \item Je nachdem, ob und wie häufig ein übergebener Parameter im Funktionsrumpf benötigt wird, können bei Lazy-Evaluation Berechnungen - \begin{itemize*} - \item komplett eingespart oder - \item (in identischer Form) wiederholt erforderlich werden - \item Unter Umständen kann man in der betreffenden Funktion durch Einführung einer temporären Variable redundante Mehrfachberechnungen einsparen ($\rightarrow$ Call by Need) - \end{itemize*} - \item Die Parameterübergabe ist bei Call by Name in der Regel aufwändiger als bei Call by Value - \begin{itemize*} - \item Die meisten Programmiersprachen (Java, C, C++, Pascal etc.) verwenden daher Call by Value ($\rightarrow$ strikte Auswertung) - \item Eine Ausnahme wird oft bei dem IF-Konstrukt gemacht (der auszuführende Code ist hier ja meist auch kein Parameter) - \end{itemize*} - \item Zu Ausdrucksstärke: während strikte Funktionen durch die Strategie Call by Value realisiert werden, ist es nicht so, dass Lazy Evaluation es erlaubt, alle nicht-strikten Funktionen zu realisieren - \begin{itemize*} - \item Die folgenden Gleichungen definieren eine nicht-strikte Multiplikation $\otimes$ auf der Basis der Multiplikation · für Zahlen: - $$0 \otimes y = 0$$ - $$x \otimes 0 = 0$$ - $$x \otimes y = x * y$$ - \item Wenn ein Arguemnt undefiniert ist, dann liefert $\otimes$ ein Ergebnis, sofern das andere Argument zu 0 evaluiert wird ($\rightarrow fak(-1) \otimes (fak(3)-6)$) - \item Implementiert werden kann die Funktion nur durch eine Art von paralleler Auswertung mit Abbruch der anderen Berechnung sobald 0 als Resultat berechnet und zurückgegeben wurde - \end{itemize*} - \item Wir betrachten nun die Beziehungen zwischen Parameterbehandlung in Programmiersprachen und Reduktion von Lambda-Termen \end{itemize*} - - \subsubsection{Auswertungsstrategien \& Programmiersprachen} - \color{blue} Werte in Programmiersprachen wie Haskell: \color{black} + \item Je nachdem, ob und wie häufig ein übergebener Parameter im Funktionsrumpf benötigt wird, können bei Lazy-Evaluation Berechnungen \begin{itemize*} - \item Primitive Werte: 2, True - \item Funktionen: ($\backslash x \rightarrow x$), ($\&\&$), ($x\rightarrow(\backslash y\rightarrow y+y)x$) + \item komplett eingespart oder + \item (in identischer Form) wiederholt erforderlich werden + \item Unter Umständen kann man in der betreffenden Funktion durch Einführung einer temporären Variable redundante Mehrfachberechnungen einsparen ($\rightarrow$ Call by Need) \end{itemize*} - - \color{blue} Werte im $\lambda$-Kalkül: - \color{black} \begin{itemize*} - \item Abstraktionen: $c_2 = \lambda s. \lambda z.s\;(s\;z),\;\;\; C_{true} = \lambda t- \lambda f.t,\;\;\; \lambda x.x,\;\;\; \newline \lambda b_1. \lambda b_2.\;b_1\; b_2 (\lambda t.\lambda f.\;f),\;\;\; \lambda x.\;(\lambda y.\;plus\; yy)x$ - \end{itemize*} - \color{blue} Auswertungsstrategie: \color{black} Keine weitere Reduzierung von Werten \newline - Reduziere keine Redexe unter Abstraktionen (umgeben von $\lambda$):\newline -$\Rightarrow$ call-by-name, call-by-value - \subsubsection{Call-By-Name} - Call-By-Name: Reduziere linkesten äußersten Redex + \item Die Parameterübergabe ist bei Call by Name in der Regel aufwändiger als bei Call by Value \begin{itemize*} - \item Aber nicht falls von einem $\lambda$ umgeben \newline - \subitem $(\lambda y. (\lambda x.y(\lambda z.z)x))\color{red} ((\lambda x.x)(\lambda y.y))$ \color{black} \newline - \subitem $(\lambda x.((\lambda \color{blue}x.x\color{black}) \color{red}(\lambda y.y)\color{black})(\lambda z.z)x)$ - \begin{center} - \item Intuition: Reduziere Argumente erst, wenn benötigt - \end{center} + \item Die meisten Programmiersprachen (Java, C, C++, Pascal etc.) verwenden daher Call by Value ($\rightarrow$ strikte Auswertung) + \item Eine Ausnahme wird oft bei dem IF-Konstrukt gemacht (der auszuführende Code ist hier ja meist auch kein Parameter) \end{itemize*} - - Auswertung in Haskell: \color{blue} Lazy-Evaluation = call-by-name (+sharing)\color{black} + \item Zu Ausdrucksstärke: während strikte Funktionen durch die Strategie Call by Value realisiert werden, ist es nicht so, dass Lazy Evaluation es erlaubt, alle nicht-strikten Funktionen zu realisieren \begin{itemize*} - \item Standard-Auswertungsstrategie für Funktionen/Konstruktoren - \item listOf x = x : listOf x - \item 3: listOf 3 $\nRightarrow$ - \item (div 1 0) : (6 : []) - \item tail ((div 1 0): (6 : [])) $\Rightarrow$ 6 : [] $\nRightarrow$ + \item Die folgenden Gleichungen definieren eine nicht-strikte Multiplikation $\otimes$ auf der Basis der Multiplikation · für Zahlen: + $$0 \otimes y = 0$$ + $$x \otimes 0 = 0$$ + $$x \otimes y = x * y$$ + \item Wenn ein Arguemnt undefiniert ist, dann liefert $\otimes$ ein Ergebnis, sofern das andere Argument zu 0 evaluiert wird ($\rightarrow fak(-1) \otimes (fak(3)-6)$) + \item Implementiert werden kann die Funktion nur durch eine Art von paralleler Auswertung mit Abbruch der anderen Berechnung sobald 0 als Resultat berechnet und zurückgegeben wurde \end{itemize*} - - \subsubsection{Call-By-Value} - Call-By-Value: Reduziere linkesten Redex - \begin{itemize*} - \item der nicht einen $\lambda$ umgibt - \item und dessen Argument ein \color{blue} Wert \color{black} ist - \subsubitem $(\lambda y.(\lambda x.y(\lambda z.z)x)((\lambda \color{blue} x.x \color{black})\color{red}(\lambda y.y)\color{black})$ - \subsubitem $\Rightarrow (\lambda \color{blue}y\color{black}(\lambda x. \color{blue}y\color{black}(\lambda z.z)x))\color{red}(\lambda y.y)\color{black}$ - \subsubitem $\Rightarrow (\lambda x.(\lambda y.y(\lambda z.z)x)) \nRightarrow$ - \item Intuition: Argumente vor Funktionsaufruf auswerten - \item Auswertungsstrategie vieler Sprachen: Java, C, Scheme, ML, ... - \item Arithmetik in Haskell: Auswertung by-value - \item $prodOf(x) = y * prodOf x$ - \item $3 * prodOf 3 \Rightarrow 3 * (3 * prodOf 3) \Rightarrow$ ... - \item $((div \space1 \space 0) * 6) * 0 \Rightarrow \bot$ - \item $((div \space2 \space 2) * 6) * 0 \Rightarrow ((1 * 6) * 0) \Rightarrow 6 * 0\Rightarrow 0 \nRightarrow$ - \end{itemize*} - - \subsubsection{Vergleich der Auswertungsstrategien} - \color{blue} Call-by-name vs. Call-by-value \color{black} - \begin{itemize*} - \item Werten nicht immer zur Normalform aus $\lambda x.(\lambda y.y)x$ - \item Gibt es Normalform, dann darauf $\beta$-reduzierbar (Church-Rosser) - \item Call-by-name terminiert öfter - \item $Y(\lambda y.z) = \lambda f. (\lambda x.f(x\;x))(\lambda x.f(x\;x))\color{red}(\lambda y.z)\color{black}$ \newline - \subitem $ \Rightarrow \lambda x.(\lambda y.z(x\;x))\color{red} (\lambda x.(\lambda y.z)(x\;x))$ \newline - \subitem $\Rightarrow (\lambda y.z)\color{red}((\lambda x.(\lambda y.z)(x\;x)) (\lambda x.(\lambda y.z (x\;x)))\color{black} \stackrel{cbn}{\Rightarrow} z$ - \subitem \newline - \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda x.(\lambda y.z)(x\;x))\color{red}(\lambda x.(\lambda y.z (x\;x))\color{black})$ - \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda y.z)((\lambda x.(\lambda y.z)(x\;x)) \color{red} (\lambda x.(\lambda y.z(x\;x)) \color{black}))$ - \end{itemize*} - \color{blue} Standardisierungssatz \newline - Wenn t eine Normalform hat, dann findet Normalreihenfolgenauswertung diese. - \color{black} - - \subsubsection{Abschließende Bemerkungen} - \begin{itemize*} - \item Der Lambda-Kalkül wurde in den dreißiger Jahren des 20. Jahrhunderts von Alonzo Church erfunden, um damit grundsätzliche Betrachtungen über berechenbare Funktionen anzustellen - \item Trotz der Einfachheit der dem Kalkül zugrunde liegenden Regeln, realisiert er ein universelles Berechnungsmodell - \item Der Lambda-Kalkül hat die Entwicklung zahlreicher, für die Informatik wichtiger Konzepte beeinflusst - \begin{itemize*} - \item Funktionale Programmiersprachen (die minimalen Funktionen von Lisp wurden auf Grundlage des Lambda-Kalküls definiert) - \item Forschund zu Typsystemen für Programmiersprachen - \item Repräsentation von Logik-Termen im Lambda-Kalkül führte zu Theorembeweisen für Logiken höherer Stufen - \end{itemize*} - \item Manche Puristen vertreten gelegentlich die Ansicht, dass funktionale Programmiersprachen nicht viel mehr sind, als "Lambda-Kalkül mit etwas syntaktischem Zucker" - \end{itemize*} - - \subsection{Zusammenfassung} - \begin{itemize*} - \item Funktionale Programmierung folgt einem verallgemeinerten Konzept der Funktionsauswertung - \item Die Programmiersprache Erlang ist dynamisch typisiert und unterstützt auch Funktionen höherer Ordnung - \item Manche Algorithmen lassen sich in Erlang aufgrund der mächtigen Listenkonstrukte und des flexiblen Pattern Matching sehr kompakt formulieren ($\rightarrow$ Potenzmenge, Quicksort) - \item Das heißt jedoch nicht, dass sehr kompakter Code auch zu sehr effizientem Laufzeit- und/oder Speicherbedarf führt - teilweise muss der Code relativ geschickt optimiert werden, um einigermaßen effiziente Lösungen zu erhalten ($\rightarrow$ Quicksort) - \item Manche Aufgaben, die in imperativen Programmiersprachen sehr effizient und einfach lösbar sind ($\rightarrow$ Teilen einer Liste in gleich große Hälften) sind mittels Listen nur recht umständlich und aufwendig lösbar - \item Es gilt in der Praxis also abzuwägen, für welche Aufgaben eine funktionale Sprache eingesetzt werden soll - \end{itemize*} - - \newpage - \section{Multithreading und parallele Programmierung} - \subsection{Grundlagen} - \subsubsection{Lernziele} - \begin{itemize*} - \item Programmierung paralleler Algorithmen und Verfahren als Paradigma - \item Verständnis grundlegender Architekturen und Modelle - \item Praktische Erfahrungen mit Erlang, Java und C++ - \end{itemize*} - - \subsubsection{Grundbegriffe} - \begin{itemize*} - \item Prozess := Programm in Ausführung; Ausführungsumgebung für ein Programm; hat eigenen Adressraum; Prozessor kann immer nur einen Prozess ausführen - \item Thread ("Faden") := leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms; "leichtgewichtig" im Vergleich zu Betriebssystemprozess; Threads eines Prozesses teilen sich Adressraum; Thread kann von CPU oder Core ausgeführt werden - \item Shared Memory := Kommunikation (über Variablen im) gemeinsamen Speicher; Prozess kann direkt auf Speicher eines anderen Prozesses zugreifen; erfordert explizite Synchronisation, z.B. über kritische Abschnitte - \item Message Passing := Prozesse mit getrennten Adressräumen; Zugriff nur auf eigenen Speicher; Kommunikation durch explizites Senden/Empfangen von Nachrichten; implizite Synchronisation durch Nachrichten - \item Parallelisierungsarten - \begin{itemize*} - \item Instruktionsparallelität: parallele Ausführung mehrerer Operationen durch eine CPU-Instruktion; explizit: Vektorinstruktionen, SIMD; implizit: Pipelining von Instruktionen - \item Taskparallelität: Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben - \item Datenparallelität: Gemeinsame Operation auf homogener Datenmenge; Zerlegung eines Datensatzes in kleinere Abschnitte - \end{itemize*} - \end{itemize*} - - \subsubsection{The free launch is over} - Taktfrequenz wächst nur noch langsam - \begin{itemize*} - \item Physikalische Gründe: Wärmeentwicklung, Energiebedarf, Kriechströme,... - \end{itemize*} - Auswege - \begin{itemize*} - \item Hyperthreading: - \begin{itemize*} - \item Abarbeitung mehrerer Threads auf einer CPU (5-15 \% Performancegewinn) - \item Einfache Hardwareunterstützung (einige Register) - \end{itemize*} - \item Multicore: - \begin{itemize*} - \item Mehrere CPUs auf einem Chip - \item Billiger als echte Mehrprozessorsysteme - \end{itemize*} - \item Caching: - \begin{itemize*} - \item Vergrößerung L1, L2, L3 Cache - \item Speicherzugriff 10-50 $\cdot$ teurer als Cachezugriff - \end{itemize*} - \end{itemize*} - - \subsubsection{Konsequenzen und Trends} - \begin{itemize*} - \item Applikationen müssen nebenläufig programmiert werden um CPU auszunutzen $\rightarrow$ Many-Core-Systeme - \item CPU-Begrenzung von Applikationen - \item Effizienz und Performanceoptimierung werden immer wichtiger - \item Unterstützung von Nebenläufigkeit/Parallelität durch Programmiersprachen - \end{itemize*} - - \begin{figure}[!tbp] - \centering - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-einordnung-programmierung} - \caption{Einordnung} - \end{minipage} - \hfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-Architekturen} - \caption{Architekturen: SIMD, SMP, NUMA, Cluster, Grid} - \end{minipage} - \vfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-Multiprozessorsysteme} - \caption{Multiprozessorsysteme} - \begin{itemize*} - \item Zugriff über Bus auf gemeinsamen Speicher - \item jeder Prozessor mit eigenen Caches - \end{itemize*} - \end{minipage} - \hfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-Multicore-Systeme} - \caption{Multicore-Systeme} - \begin{itemize*} - \item mehrere Prozessorkerne auf einem Chip - \item Kerne typischerweise mit eigenen L1/L2-Caches und gemeinsamen L3-Cache - \end{itemize*} - \end{minipage} - \vfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-SMP} - \caption{SMP (Symmetric Multi Processing)} - \begin{itemize*} - \item Speicherbandbreite begrenzt und von allen Prozessoren gemeinsam genutzt - \item Skalierbarkeit begrenzt - \item Single Socket Lösung - \end{itemize*} - \end{minipage} - \hfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-NUMA} - \caption{NUMA (Non-Uniform Memory Access)} - \begin{itemize*} - \item jedem Prozessor sind Teile des Speichers zugeordnet - \item lokaler Zugriff ist schneller als entfernter - \item Typisch für Multi-Socket Systeme - \end{itemize*} - \end{minipage} - \end{figure} - - \subsubsection{Symmetrisch vs. Nicht-symmetrisch} - \begin{tabular}{c | c} - SMP (Symmetric Multi Processing) & NUMA (Non-Uniform Memory-Access) \\ - Speicherbandbreite begrenzt und von allen Prozessoren gemeinsam genutzt & jedem Prozessor sind Teile des Speichers zugeordnet \\ - Skalierbarkeit begrenzt & lokaler Zugriff ist schneller als entfernter \\ - Single Socket-Lösung & Mehr-Socket-Board \\ - \end{tabular} - - \subsubsection{CPU vs. GPU} - \begin{itemize*} - \item GPU = Graphics Processing Units - \item Hochparallele Prozessorarchitekturen (nicht nur) für Grafikrendering - \end{itemize*} - \begin{center} - \includegraphics[width=0.95\linewidth]{Assets/Programmierparadigmen-CPU-vs-GPU} - \end{center} - - \subsubsection{Flynn's Architekturklassifikation} - \begin{center} - \includegraphics[width=0.95\linewidth]{Assets/Programmierparadigmen-flynn-architekturklassifikation} - \end{center} - - \subsubsection{Maße zur Leistungsbewertung} - \begin{itemize*} - \item Maße für Laufzeitgewinn durch Parallelisierung - \item $T_n$ = Laufzeit des Programms mit n Prozessoren/Kernen - \item Speedup $Speedup = \frac{T_1}{T_n}$ - \item Effizienz $Effizienz = \frac{Speedup}{n}$ - \end{itemize*} - - \subsubsection{Amdahlsches Gesetz} - \begin{itemize*} - \item Berücksichtigung parallelisierbarer und serieller Anteile im Programmablauf - \item p = paralleler Anteil - \item s = serieller Anteil - \item n Prozessoren - \item $p+s = 1$ - \item Maximaler Speedup $Speedup_{max} = \frac{T_1}{T_n} = {\frac{s+p}{s+ \frac{p}{n}}} = \frac{1}{s+\frac{p}{n}}$ - \end{itemize*} - \begin{center} - \includegraphics[width=0.35\linewidth]{Assets/Programmierparadigmen-parallelisierung} - \includegraphics[width=0.35\linewidth]{Assets/Programmierparadigmen-amdahlsches-gesetz} - \end{center} - - \subsubsection{Prozesse und Threads} - Prozess := Programm in Ausführung; Ausführungsumgebung für ein Programm - \begin{itemize*} - \item hat eigenen Adressraum - \item Prozessor kann immer nur einen Prozess ausführen - \end{itemize*} - Thread ("Faden") := leichtgewichtige Ausführungsreinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms - \begin{itemize*} - \item leichtgewichtig im Vergleich zu Betriebssystemprozess - \item Threads eines Prozesses teilen sich den Adressraum - \item Thread kann von einer CPU oder einem Core ausgeführt werden - \end{itemize*} - - \subsubsection{Shared Memory vs Message Passing} - Art der Kommunikation zwischen Prozessen oder Threads - \paragraph{Shared Memory} - \begin{itemize*} - \item Kommunikation (über Variable im) gemeinsamen Speicher - \item Prozess kann direkt auf Speicher eines anderen Prozesses zugreifen - \item erfordert explizite Synchronisation, z.B. über zeitkritische Abschnitte - \end{itemize*} - \paragraph{Message Passing} - \begin{itemize*} - \item Prozesse mit getrennten Adressräumen; Zugriff nur auf eigenen Speicher - \item Kommunikation durch explizites Senden/Empfangen von Nachrichten - \end{itemize*} - - \begin{figure}[!tbp] - \centering - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-Shared-Memory-vs-Message-Passing} - \caption{Shared Memory vs Message Passing} - \end{minipage} - \hfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-Programmiermodelle} - \caption{Programmiermodelle} - \end{minipage} - \end{figure} - - \subsubsection{Parallelisierungsarten} - Instruktionsparallelität: - \begin{description*} - \item[parallele Ausführung] mehrerer Operationen durch eine CPU-Instruktion - \item[explizit] Vektorinstruktionen, SIMD - \item[implizit] Pipelining von Instruktionen - \item[Taskparallelität] Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben - \item[Datenparalelität] \hfill - \begin{itemize*} - \item Gemeinsame Operation auf homogener Datenmenge - \item Zerlegung eines Datensatzes in kleinere Abschnitte - \end{itemize*} - \end{description*} + \item Wir betrachten nun die Beziehungen zwischen Parameterbehandlung in Programmiersprachen und Reduktion von Lambda-Termen +\end{itemize*} - \begin{figure}[!tbp] - \centering - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-Instruktionsparallelität} - \caption{Instruktionsparallelität: SIMD} - \begin{itemize*} - \item Autovektorisierung durch Compiler - \item explizite Instruktionen - \item Beispiel: Addition zweier Vektoren - \end{itemize*} - \end{minipage} - \hfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-Taskparallelität} - \caption{Taskparallelität} - \begin{itemize*} - \item Unabhängikeit von Teilprozessen $\rightarrow$ Desequentialisierung - \item Beispiel: Quicksort - \end{itemize*} - \end{minipage} - \vfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-Datenparallelität} - \caption{Datenparallelität} - \begin{itemize*} - \item homogene Datenmenge: Felder, Listen, Dokumentenmenge,... - \item Verteilung der Daten - \item alle Prozessoren führen gleiches Programm auf jeweils eigenen Daten aus - \item Beispiel: Matrixaddition S = A + B - \end{itemize*} - \end{minipage} - \end{figure} - - \subsubsection{Herausforderungen} +\subsubsection{Auswertungsstrategien \& Programmiersprachen} +\color{blue} Werte in Programmiersprachen wie Haskell: \color{black} +\begin{itemize*} + \item Primitive Werte: 2, True + \item Funktionen: ($\backslash x \rightarrow x$), ($\&\&$), ($x\rightarrow(\backslash y\rightarrow y+y)x$) +\end{itemize*} + +\color{blue} Werte im $\lambda$-Kalkül: +\color{black} \begin{itemize*} + \item Abstraktionen: $c_2 = \lambda s. \lambda z.s\;(s\;z),\;\;\; C_{true} = \lambda t- \lambda f.t,\;\;\; \lambda x.x,\;\;\; \newline \lambda b_1. \lambda b_2.\;b_1\; b_2 (\lambda t.\lambda f.\;f),\;\;\; \lambda x.\;(\lambda y.\;plus\; yy)x$ +\end{itemize*} +\color{blue} Auswertungsstrategie: \color{black} Keine weitere Reduzierung von Werten \newline +Reduziere keine Redexe unter Abstraktionen (umgeben von $\lambda$):\newline +$\Rightarrow$ call-by-name, call-by-value +\subsubsection{Call-By-Name} +Call-By-Name: Reduziere linkesten äußersten Redex +\begin{itemize*} + \item Aber nicht falls von einem $\lambda$ umgeben \newline + \subitem $(\lambda y. (\lambda x.y(\lambda z.z)x))\color{red} ((\lambda x.x)(\lambda y.y))$ \color{black} \newline + \subitem $(\lambda x.((\lambda \color{blue}x.x\color{black}) \color{red}(\lambda y.y)\color{black})(\lambda z.z)x)$ + \begin{center} + \item Intuition: Reduziere Argumente erst, wenn benötigt + \end{center} +\end{itemize*} + +Auswertung in Haskell: \color{blue} Lazy-Evaluation = call-by-name (+sharing)\color{black} +\begin{itemize*} + \item Standard-Auswertungsstrategie für Funktionen/Konstruktoren + \item listOf x = x : listOf x + \item 3: listOf 3 $\nRightarrow$ + \item (div 1 0) : (6 : []) + \item tail ((div 1 0): (6 : [])) $\Rightarrow$ 6 : [] $\nRightarrow$ +\end{itemize*} + +\subsubsection{Call-By-Value} +Call-By-Value: Reduziere linkesten Redex +\begin{itemize*} + \item der nicht einen $\lambda$ umgibt + \item und dessen Argument ein \color{blue} Wert \color{black} ist + \subsubitem $(\lambda y.(\lambda x.y(\lambda z.z)x)((\lambda \color{blue} x.x \color{black})\color{red}(\lambda y.y)\color{black})$ + \subsubitem $\Rightarrow (\lambda \color{blue}y\color{black}(\lambda x. \color{blue}y\color{black}(\lambda z.z)x))\color{red}(\lambda y.y)\color{black}$ + \subsubitem $\Rightarrow (\lambda x.(\lambda y.y(\lambda z.z)x)) \nRightarrow$ + \item Intuition: Argumente vor Funktionsaufruf auswerten + \item Auswertungsstrategie vieler Sprachen: Java, C, Scheme, ML, ... + \item Arithmetik in Haskell: Auswertung by-value + \item $prodOf(x) = y * prodOf x$ + \item $3 * prodOf 3 \Rightarrow 3 * (3 * prodOf 3) \Rightarrow$ ... + \item $((div \space1 \space 0) * 6) * 0 \Rightarrow \bot$ + \item $((div \space2 \space 2) * 6) * 0 \Rightarrow ((1 * 6) * 0) \Rightarrow 6 * 0\Rightarrow 0 \nRightarrow$ +\end{itemize*} + +\subsubsection{Vergleich der Auswertungsstrategien} +\color{blue} Call-by-name vs. Call-by-value \color{black} +\begin{itemize*} + \item Werten nicht immer zur Normalform aus $\lambda x.(\lambda y.y)x$ + \item Gibt es Normalform, dann darauf $\beta$-reduzierbar (Church-Rosser) + \item Call-by-name terminiert öfter + \item $Y(\lambda y.z) = \lambda f. (\lambda x.f(x\;x))(\lambda x.f(x\;x))\color{red}(\lambda y.z)\color{black}$ \newline + \subitem $ \Rightarrow \lambda x.(\lambda y.z(x\;x))\color{red} (\lambda x.(\lambda y.z)(x\;x))$ \newline + \subitem $\Rightarrow (\lambda y.z)\color{red}((\lambda x.(\lambda y.z)(x\;x)) (\lambda x.(\lambda y.z (x\;x)))\color{black} \stackrel{cbn}{\Rightarrow} z$ + \subitem \newline + \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda x.(\lambda y.z)(x\;x))\color{red}(\lambda x.(\lambda y.z (x\;x))\color{black})$ + \subitem $\stackrel{cbv}{\Rightarrow} (\lambda y.z)((\lambda y.z)((\lambda x.(\lambda y.z)(x\;x)) \color{red} (\lambda x.(\lambda y.z(x\;x)) \color{black}))$ +\end{itemize*} +\color{blue} Standardisierungssatz \newline +Wenn t eine Normalform hat, dann findet Normalreihenfolgenauswertung diese. +\color{black} + +\subsubsection{Abschließende Bemerkungen} +\begin{itemize*} + \item Der Lambda-Kalkül wurde in den dreißiger Jahren des 20. Jahrhunderts von Alonzo Church erfunden, um damit grundsätzliche Betrachtungen über berechenbare Funktionen anzustellen + \item Trotz der Einfachheit der dem Kalkül zugrunde liegenden Regeln, realisiert er ein universelles Berechnungsmodell + \item Der Lambda-Kalkül hat die Entwicklung zahlreicher, für die Informatik wichtiger Konzepte beeinflusst \begin{itemize*} - \item Zerlegung eines Problems in parallel verarbeitbare Teile - \begin{itemize*} - \item Beispiel: Suche in einer Datenbank mit 1 TB Größe - \item Annahme: 100 MB/Sekunde mit einem Prozessor = 175 Minuten - \item bei paralleler Suche durch 10 Prozessoren = 17.5 Minuten - \item Übertragbar auf andere Probleme, z.B. Sortieren, Suche in Graphen? - \end{itemize*} - \item Synchronisation konkurrierender Zugriffe auf gemeinsame Ressourcen - \begin{itemize*} - \item Beispiel: Produzent-Konsument-Beziehung - \item Annahme: Datenaustausch über gemeinsame Liste - \item Fragestellungen: Benachrichtigung über neues Element in der Liste, Konsument entnimmt Element während Produzent einfügt - \item Wechselseitiger Ausschluss - \end{itemize*} - \item außerdem: Fehlersuche, Optimierung + \item Funktionale Programmiersprachen (die minimalen Funktionen von Lisp wurden auf Grundlage des Lambda-Kalküls definiert) + \item Forschund zu Typsystemen für Programmiersprachen + \item Repräsentation von Logik-Termen im Lambda-Kalkül führte zu Theorembeweisen für Logiken höherer Stufen \end{itemize*} - - \subsubsection{Zusammenfassung} + \item Manche Puristen vertreten gelegentlich die Ansicht, dass funktionale Programmiersprachen nicht viel mehr sind, als "Lambda-Kalkül mit etwas syntaktischem Zucker" +\end{itemize*} + +\subsection{Zusammenfassung} +\begin{itemize*} + \item Funktionale Programmierung folgt einem verallgemeinerten Konzept der Funktionsauswertung + \item Die Programmiersprache Erlang ist dynamisch typisiert und unterstützt auch Funktionen höherer Ordnung + \item Manche Algorithmen lassen sich in Erlang aufgrund der mächtigen Listenkonstrukte und des flexiblen Pattern Matching sehr kompakt formulieren ($\rightarrow$ Potenzmenge, Quicksort) + \item Das heißt jedoch nicht, dass sehr kompakter Code auch zu sehr effizientem Laufzeit- und/oder Speicherbedarf führt - teilweise muss der Code relativ geschickt optimiert werden, um einigermaßen effiziente Lösungen zu erhalten ($\rightarrow$ Quicksort) + \item Manche Aufgaben, die in imperativen Programmiersprachen sehr effizient und einfach lösbar sind ($\rightarrow$ Teilen einer Liste in gleich große Hälften) sind mittels Listen nur recht umständlich und aufwendig lösbar + \item Es gilt in der Praxis also abzuwägen, für welche Aufgaben eine funktionale Sprache eingesetzt werden soll +\end{itemize*} + +\newpage +\section{Multithreading und parallele Programmierung} +\subsection{Grundlagen} +\subsubsection{Lernziele} +\begin{itemize*} + \item Programmierung paralleler Algorithmen und Verfahren als Paradigma + \item Verständnis grundlegender Architekturen und Modelle + \item Praktische Erfahrungen mit Erlang, Java und C++ +\end{itemize*} + +\subsubsection{Grundbegriffe} +\begin{itemize*} + \item Prozess := Programm in Ausführung; Ausführungsumgebung für ein Programm; hat eigenen Adressraum; Prozessor kann immer nur einen Prozess ausführen + \item Thread ("Faden") := leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms; "leichtgewichtig" im Vergleich zu Betriebssystemprozess; Threads eines Prozesses teilen sich Adressraum; Thread kann von CPU oder Core ausgeführt werden + \item Shared Memory := Kommunikation (über Variablen im) gemeinsamen Speicher; Prozess kann direkt auf Speicher eines anderen Prozesses zugreifen; erfordert explizite Synchronisation, z.B. über kritische Abschnitte + \item Message Passing := Prozesse mit getrennten Adressräumen; Zugriff nur auf eigenen Speicher; Kommunikation durch explizites Senden/Empfangen von Nachrichten; implizite Synchronisation durch Nachrichten + \item Parallelisierungsarten \begin{itemize*} - \item Parallele Verarbeitung als wichtiges Paradigma moderner Software - \item verschiedene parallele - \begin{itemize*} - \item Hardwarearchitekturen und - \item Programmiermodelle - \end{itemize*} - \item Herausforderungen - \begin{itemize*} - \item Problemzerlegung - \item Synchronisation - \item ... - \end{itemize*} - \item im Weiteren: konkrete Methoden und Techniken in Erlang und C++ + \item Instruktionsparallelität: parallele Ausführung mehrerer Operationen durch eine CPU-Instruktion; explizit: Vektorinstruktionen, SIMD; implizit: Pipelining von Instruktionen + \item Taskparallelität: Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben + \item Datenparallelität: Gemeinsame Operation auf homogener Datenmenge; Zerlegung eines Datensatzes in kleinere Abschnitte \end{itemize*} - - \subsection{Parallele Programmierung in Erlang} - \subsubsection{Unterstützung paralleler Programmierung in Erlang} +\end{itemize*} + +\subsubsection{The free launch is over} +Taktfrequenz wächst nur noch langsam +\begin{itemize*} + \item Physikalische Gründe: Wärmeentwicklung, Energiebedarf, Kriechströme,... +\end{itemize*} +Auswege +\begin{itemize*} + \item Hyperthreading: \begin{itemize*} - \item Leichtgewichtige Prozesse und Message Passing - \item SMP-Support (Symmetric Multi Processing) - \item Ziele für effiziente Parallelisierung - \begin{itemize*} - \item Problem in viele Prozesse zerlegen (aber nicht zu viele ...) - \item Seiteneffekte vermeiden (würde Synchronisation erfordern ...) - \item Sequentiellen Flaschenhals vermeiden (Zugriff auf gemeinsame Ressourcen: IO, Registrierung von Prozessen, ...) -> Small Messages, Big Computation! - \end{itemize*} + \item Abarbeitung mehrerer Threads auf einer CPU (5-15 \% Performancegewinn) + \item Einfache Hardwareunterstützung (einige Register) \end{itemize*} - - \subsubsection{Prozesse in Erlang} + \item Multicore: \begin{itemize*} - \item Erlang VM = Betriebssystemprozess - \item Erlang-Prozess = Thread innerhalb der Erlang VM + \item Mehrere CPUs auf einem Chip + \item Billiger als echte Mehrprozessorsysteme + \end{itemize*} + \item Caching: + \begin{itemize*} + \item Vergrößerung L1, L2, L3 Cache + \item Speicherzugriff 10-50 $\cdot$ teurer als Cachezugriff + \end{itemize*} +\end{itemize*} + +\subsubsection{Konsequenzen und Trends} +\begin{itemize*} + \item Applikationen müssen nebenläufig programmiert werden um CPU auszunutzen $\rightarrow$ Many-Core-Systeme + \item CPU-Begrenzung von Applikationen + \item Effizienz und Performanceoptimierung werden immer wichtiger + \item Unterstützung von Nebenläufigkeit/Parallelität durch Programmiersprachen +\end{itemize*} + +\begin{figure}[!tbp] + \centering + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-einordnung-programmierung} + \caption{Einordnung} + \end{minipage} + \hfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-Architekturen} + \caption{Architekturen: SIMD, SMP, NUMA, Cluster, Grid} + \end{minipage} + \vfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-Multiprozessorsysteme} + \caption{Multiprozessorsysteme} \begin{itemize*} - \item kein Zugriff auf gemeinsame Daten, daher "Prozess" + \item Zugriff über Bus auf gemeinsamen Speicher + \item jeder Prozessor mit eigenen Caches \end{itemize*} - \item jede Erlang-Funktion kann einen Prozess bilden - \item Funktion spawn erzeugt einen Prozess, der die Funktion Fun ausführt - \begin{lstlisting} + \end{minipage} + \hfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-Multicore-Systeme} + \caption{Multicore-Systeme} + \begin{itemize*} + \item mehrere Prozessorkerne auf einem Chip + \item Kerne typischerweise mit eigenen L1/L2-Caches und gemeinsamen L3-Cache + \end{itemize*} + \end{minipage} + \vfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-SMP} + \caption{SMP (Symmetric Multi Processing)} + \begin{itemize*} + \item Speicherbandbreite begrenzt und von allen Prozessoren gemeinsam genutzt + \item Skalierbarkeit begrenzt + \item Single Socket Lösung + \end{itemize*} + \end{minipage} + \hfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=1.0\linewidth]{Assets/Programmierparadigmen-NUMA} + \caption{NUMA (Non-Uniform Memory Access)} + \begin{itemize*} + \item jedem Prozessor sind Teile des Speichers zugeordnet + \item lokaler Zugriff ist schneller als entfernter + \item Typisch für Multi-Socket Systeme + \end{itemize*} + \end{minipage} +\end{figure} + +\subsubsection{Symmetrisch vs. Nicht-symmetrisch} +\begin{tabular}{c | c} + SMP (Symmetric Multi Processing) & NUMA (Non-Uniform Memory-Access) \\ + Speicherbandbreite begrenzt und von allen Prozessoren gemeinsam genutzt & jedem Prozessor sind Teile des Speichers zugeordnet \\ + Skalierbarkeit begrenzt & lokaler Zugriff ist schneller als entfernter \\ + Single Socket-Lösung & Mehr-Socket-Board \\ +\end{tabular} + +\subsubsection{CPU vs. GPU} +\begin{itemize*} + \item GPU = Graphics Processing Units + \item Hochparallele Prozessorarchitekturen (nicht nur) für Grafikrendering +\end{itemize*} +\begin{center} + \includegraphics[width=0.95\linewidth]{Assets/Programmierparadigmen-CPU-vs-GPU} +\end{center} + +\subsubsection{Flynn's Architekturklassifikation} +\begin{center} + \includegraphics[width=0.95\linewidth]{Assets/Programmierparadigmen-flynn-architekturklassifikation} +\end{center} + +\subsubsection{Maße zur Leistungsbewertung} +\begin{itemize*} + \item Maße für Laufzeitgewinn durch Parallelisierung + \item $T_n$ = Laufzeit des Programms mit n Prozessoren/Kernen + \item Speedup $Speedup = \frac{T_1}{T_n}$ + \item Effizienz $Effizienz = \frac{Speedup}{n}$ +\end{itemize*} + +\subsubsection{Amdahlsches Gesetz} +\begin{itemize*} + \item Berücksichtigung parallelisierbarer und serieller Anteile im Programmablauf + \item p = paralleler Anteil + \item s = serieller Anteil + \item n Prozessoren + \item $p+s = 1$ + \item Maximaler Speedup $Speedup_{max} = \frac{T_1}{T_n} = {\frac{s+p}{s+ \frac{p}{n}}} = \frac{1}{s+\frac{p}{n}}$ +\end{itemize*} +\begin{center} + \includegraphics[width=0.35\linewidth]{Assets/Programmierparadigmen-parallelisierung} + \includegraphics[width=0.35\linewidth]{Assets/Programmierparadigmen-amdahlsches-gesetz} +\end{center} + +\subsubsection{Prozesse und Threads} +Prozess := Programm in Ausführung; Ausführungsumgebung für ein Programm +\begin{itemize*} + \item hat eigenen Adressraum + \item Prozessor kann immer nur einen Prozess ausführen +\end{itemize*} +Thread ("Faden") := leichtgewichtige Ausführungsreinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms +\begin{itemize*} + \item leichtgewichtig im Vergleich zu Betriebssystemprozess + \item Threads eines Prozesses teilen sich den Adressraum + \item Thread kann von einer CPU oder einem Core ausgeführt werden +\end{itemize*} + +\subsubsection{Shared Memory vs Message Passing} +Art der Kommunikation zwischen Prozessen oder Threads +\paragraph{Shared Memory} +\begin{itemize*} + \item Kommunikation (über Variable im) gemeinsamen Speicher + \item Prozess kann direkt auf Speicher eines anderen Prozesses zugreifen + \item erfordert explizite Synchronisation, z.B. über zeitkritische Abschnitte +\end{itemize*} +\paragraph{Message Passing} +\begin{itemize*} + \item Prozesse mit getrennten Adressräumen; Zugriff nur auf eigenen Speicher + \item Kommunikation durch explizites Senden/Empfangen von Nachrichten +\end{itemize*} + +\begin{figure}[!tbp] + \centering + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Shared-Memory-vs-Message-Passing} + \caption{Shared Memory vs Message Passing} + \end{minipage} + \hfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-Programmiermodelle} + \caption{Programmiermodelle} + \end{minipage} +\end{figure} + +\subsubsection{Parallelisierungsarten} +Instruktionsparallelität: +\begin{description*} + \item[parallele Ausführung] mehrerer Operationen durch eine CPU-Instruktion + \item[explizit] Vektorinstruktionen, SIMD + \item[implizit] Pipelining von Instruktionen + \item[Taskparallelität] Ausnutzung inhärenter Parallelität durch simultane Ausführung unabhängiger Aufgaben + \item[Datenparalelität] \hfill + \begin{itemize*} + \item Gemeinsame Operation auf homogener Datenmenge + \item Zerlegung eines Datensatzes in kleinere Abschnitte + \end{itemize*} +\end{description*} + +\begin{figure}[!tbp] + \centering + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-Instruktionsparallelität} + \caption{Instruktionsparallelität: SIMD} + \begin{itemize*} + \item Autovektorisierung durch Compiler + \item explizite Instruktionen + \item Beispiel: Addition zweier Vektoren + \end{itemize*} + \end{minipage} + \hfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Taskparallelität} + \caption{Taskparallelität} + \begin{itemize*} + \item Unabhängikeit von Teilprozessen $\rightarrow$ Desequentialisierung + \item Beispiel: Quicksort + \end{itemize*} + \end{minipage} + \vfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-Datenparallelität} + \caption{Datenparallelität} + \begin{itemize*} + \item homogene Datenmenge: Felder, Listen, Dokumentenmenge,... + \item Verteilung der Daten + \item alle Prozessoren führen gleiches Programm auf jeweils eigenen Daten aus + \item Beispiel: Matrixaddition S = A + B + \end{itemize*} + \end{minipage} +\end{figure} + +\subsubsection{Herausforderungen} +\begin{itemize*} + \item Zerlegung eines Problems in parallel verarbeitbare Teile + \begin{itemize*} + \item Beispiel: Suche in einer Datenbank mit 1 TB Größe + \item Annahme: 100 MB/Sekunde mit einem Prozessor = 175 Minuten + \item bei paralleler Suche durch 10 Prozessoren = 17.5 Minuten + \item Übertragbar auf andere Probleme, z.B. Sortieren, Suche in Graphen? + \end{itemize*} + \item Synchronisation konkurrierender Zugriffe auf gemeinsame Ressourcen + \begin{itemize*} + \item Beispiel: Produzent-Konsument-Beziehung + \item Annahme: Datenaustausch über gemeinsame Liste + \item Fragestellungen: Benachrichtigung über neues Element in der Liste, Konsument entnimmt Element während Produzent einfügt + \item Wechselseitiger Ausschluss + \end{itemize*} + \item außerdem: Fehlersuche, Optimierung +\end{itemize*} + +\subsubsection{Zusammenfassung} +\begin{itemize*} + \item Parallele Verarbeitung als wichtiges Paradigma moderner Software + \item verschiedene parallele + \begin{itemize*} + \item Hardwarearchitekturen und + \item Programmiermodelle + \end{itemize*} + \item Herausforderungen + \begin{itemize*} + \item Problemzerlegung + \item Synchronisation + \item ... + \end{itemize*} + \item im Weiteren: konkrete Methoden und Techniken in Erlang und C++ +\end{itemize*} + +\subsection{Parallele Programmierung in Erlang} +\subsubsection{Unterstützung paralleler Programmierung in Erlang} +\begin{itemize*} + \item Leichtgewichtige Prozesse und Message Passing + \item SMP-Support (Symmetric Multi Processing) + \item Ziele für effiziente Parallelisierung + \begin{itemize*} + \item Problem in viele Prozesse zerlegen (aber nicht zu viele ...) + \item Seiteneffekte vermeiden (würde Synchronisation erfordern ...) + \item Sequentiellen Flaschenhals vermeiden (Zugriff auf gemeinsame Ressourcen: IO, Registrierung von Prozessen, ...) -> Small Messages, Big Computation! + \end{itemize*} +\end{itemize*} + +\subsubsection{Prozesse in Erlang} +\begin{itemize*} + \item Erlang VM = Betriebssystemprozess + \item Erlang-Prozess = Thread innerhalb der Erlang VM + \begin{itemize*} + \item kein Zugriff auf gemeinsame Daten, daher "Prozess" + \end{itemize*} + \item jede Erlang-Funktion kann einen Prozess bilden + \item Funktion spawn erzeugt einen Prozess, der die Funktion Fun ausführt + \begin{lstlisting} Pid = spawn(fun Fun/0) \end{lstlisting} - \item Resultat = Prozessidentifikation Pid, mittels der man dem Prozess Nachrichten schicken kann. - \item über self() kann man die eigene Pid ermitteln - \item Übergabe von Arghumenten an den Prozess bei der Erzeugung - \begin{lstlisting} + \item Resultat = Prozessidentifikation Pid, mittels der man dem Prozess Nachrichten schicken kann. + \item über self() kann man die eigene Pid ermitteln + \item Übergabe von Arghumenten an den Prozess bei der Erzeugung + \begin{lstlisting} Pid = spawn(fun() $\rightarrow$ any_func(Arg1, Arg2, ...) end) \end{lstlisting} - \end{itemize*} - - \begin{figure}[!tbp] - \centering - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-erlang-Beispiele} - \caption{Beispiele} - \end{minipage} - \hfill - \begin{minipage}[b]{0.45\textwidth} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-SMP Erlang} - \caption{SMP Erlang} - \end{minipage} - \end{figure} +\end{itemize*} +\begin{figure}[!tbp] + \centering + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-erlang-Beispiele} + \caption{Beispiele} + \end{minipage} + \hfill + \begin{minipage}[b]{0.45\textwidth} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-SMP Erlang} + \caption{SMP Erlang} + \end{minipage} +\end{figure} + +\begin{itemize*} + \item 4 Betriebssystemthreads (hier 2 Kerne mit Hyperthreading) + \item kann mit -smp [disable $\mid$ enable $\mid$ auto] beeinflusst werden + \item +S [Anzahl] bestimmt Anzahl der Scheduler \begin{itemize*} - \item 4 Betriebssystemthreads (hier 2 Kerne mit Hyperthreading) - \item kann mit -smp [disable $\mid$ enable $\mid$ auto] beeinflusst werden - \item +S [Anzahl] bestimmt Anzahl der Scheduler - \begin{itemize*} - \item sollte nicht größer als Anzahl der Kerne/Prozessoren sein - \end{itemize*} + \item sollte nicht größer als Anzahl der Kerne/Prozessoren sein \end{itemize*} - - \subsubsection{Scheduler in Erlang} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-scheduler} - \end{center} - - \subsubsection{Message Passing in Erlang: Senden einer Nachricht} - \begin{lstlisting} +\end{itemize*} + +\subsubsection{Scheduler in Erlang} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-scheduler} +\end{center} + +\subsubsection{Message Passing in Erlang: Senden einer Nachricht} +\begin{lstlisting} Pid ! Message \end{lstlisting} - \begin{itemize*} - \item an Prozess Pid wird die Nachricht Message gesendet - \item der Prozess muss eine Empfangsoperation ausführen. damit ihn die Nachricht erreichen kann - \begin{lstlisting}[language=erlang] +\begin{itemize*} + \item an Prozess Pid wird die Nachricht Message gesendet + \item der Prozess muss eine Empfangsoperation ausführen. damit ihn die Nachricht erreichen kann + \begin{lstlisting}[language=erlang] receive Pattern1 [when Guard1] -> Expressions1; Pattern2 [when Guard2] -> Expressions2; ... end \end{lstlisting} - \item trifft eine Nachricht ein, wird versucht, diese mit einem Pattern und ggf. vorhandenen Guard zu "matchen" - \item erstes zutreffendes Pattern (inkl. Guard) bestimmt, welcher Ausdruck ausgewertet wird - \item trifft kein Pattern zu, wird die Nachricht für spätere Verwendung aufgehoben und Prozess wartet auf die nächste Nachricht ($\rightarrow$ selective receive) - \end{itemize*} - - - \subsubsection{Ein einfacher Echo-Server} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-02} - \end{center} - - Erklärungen + \item trifft eine Nachricht ein, wird versucht, diese mit einem Pattern und ggf. vorhandenen Guard zu "matchen" + \item erstes zutreffendes Pattern (inkl. Guard) bestimmt, welcher Ausdruck ausgewertet wird + \item trifft kein Pattern zu, wird die Nachricht für spätere Verwendung aufgehoben und Prozess wartet auf die nächste Nachricht ($\rightarrow$ selective receive) +\end{itemize*} + + +\subsubsection{Ein einfacher Echo-Server} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-02} +\end{center} + +Erklärungen +\begin{itemize*} + \item Funktion loop() realisiert einen (nur bedingt nützlichen) Echo-Dienst, der jede empfangene Nachricht unverändert an den Absender zurückschickt, bis er nach Empfang von stop endet + \item Funktion run() + \begin{enumerate*} + \item startet den Echoserver (Zeile 4) + \item schickt ihm als nächstes eine Nachricht (Zeile 5) + \item wartet auf eine Antwort (Zeile 6) + \item gibt diese aus (Zeile 7) + \item schickt dann stop an den Echoserver (Zeile 9) + \end{enumerate*} + \item Aufruf in der Funktion loop() erfolgt endrekursiv, daher wird kein wachsender Aufrufstapel angelegt (Hinweis: grundsätzlich zu beachten, da sonst der Speicherbedarf stetig wächst) +\end{itemize*} + +\subsubsection{Ansätze zur Parallelisierung} +\begin{itemize*} + \item Beispiel: Berechnung einer (zufällig generierten) Liste von Fibonaccizahlen + \item Sequentielle Lösung über lists:map/2 +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-03} +\end{center} + +\subsubsection{pmap: Parallele Funktionen höherer Ordnung} +\begin{itemize*} + \item Parallele Variante von lists:map(Fun, list) + \item für jedes Listenelement einen Prozess erzeugen + \item Ergebnisse einsammeln +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-04} +\end{center} + +\paragraph{pmap: Hilfsfunktionen} + +\color{orange} Eigentliche Verarbeitungsfunktion ausführen \color{black} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-05} +\end{center} +\begin{itemize*} + \item Funktion F aufrufen, catch sorgt für korrekte Behandlung von Fehlern in F + \item Ergebnis zusammen mit eigener Pid (self()) an Elternprozess senden +\end{itemize*} + +\color{orange} Einsammeln der Ergebnisse \color{black} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-06} +\end{center} +\begin{itemize*} + \item Zeile 5: Warten bis Paar (Pid, Ergebniswert) eintrifft + \item Zeile 7: Tail ist leer $\rightarrow$ alle Ergebnisse eingetroffen +\end{itemize*} + +\paragraph{Parallele Berechnung der Fibonacci-Zahlen} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-07} +\end{center} + +\paragraph{Diskussion} + +\begin{itemize*} + \item Passende Abstraktion wählen \begin{itemize*} - \item Funktion loop() realisiert einen (nur bedingt nützlichen) Echo-Dienst, der jede empfangene Nachricht unverändert an den Absender zurückschickt, bis er nach Empfang von stop endet - \item Funktion run() - \begin{enumerate*} - \item startet den Echoserver (Zeile 4) - \item schickt ihm als nächstes eine Nachricht (Zeile 5) - \item wartet auf eine Antwort (Zeile 6) - \item gibt diese aus (Zeile 7) - \item schickt dann stop an den Echoserver (Zeile 9) - \end{enumerate*} - \item Aufruf in der Funktion loop() erfolgt endrekursiv, daher wird kein wachsender Aufrufstapel angelegt (Hinweis: grundsätzlich zu beachten, da sonst der Speicherbedarf stetig wächst) + \item Ist Ordnung der Ergebnisse notwendig? + \item Werden Ergebnisse benötigt? \end{itemize*} - - \subsubsection{Ansätze zur Parallelisierung} + \item Anzahl der parallelen Prozesse \begin{itemize*} - \item Beispiel: Berechnung einer (zufällig generierten) Liste von Fibonaccizahlen - \item Sequentielle Lösung über lists:map/2 + \item Abhängig von Berechnungsmodell, Hardware etc. + \item evtl. pmap mit max. Anzahl gleichzeitiger Prozesse \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-03} - \end{center} - - \subsubsection{pmap: Parallele Funktionen höherer Ordnung} + \item Berechnungsaufwand der Prozesse \begin{itemize*} - \item Parallele Variante von lists:map(Fun, list) - \item für jedes Listenelement einen Prozess erzeugen - \item Ergebnisse einsammeln + \item Berechnung vs. Daten/Ergebnisse senden \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-04} - \end{center} - - \paragraph{pmap: Hilfsfunktionen} - - \color{orange} Eigentliche Verarbeitungsfunktion ausführen \color{black} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-05} - \end{center} +\end{itemize*} + +\paragraph{pmap: Alternative Implementierung} + +\begin{itemize*} + \item ohne Berücksichtigung der Ordnung der Ergebnismenge + \item Zählen für die bereits eingetroffenen Ergebnisse +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-08} +\end{center} + +\subsubsection{Speedup} +Bestimmung des Speedups erfordert +\begin{itemize*} + \item Zeitmessung + \item Kontrolle der genutzten Prozessoren/Cores +\end{itemize*} +\color{orange} Welchen Einfluss hat die Zahl der erzeugten Prozesse. \color{black} + +\paragraph{Speedup: Zeitmessung} + +Nutzung der Funktion timer:tc/3 +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-09} +\end{center} +Für bessere Aussagekraft: mehrfache Ausführung +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-10} +\end{center} + +\paragraph{Bestimmung: Speedup} + +ch4\_6:benchmark(ch4\_4, run, 1000). +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Speedup-Bestimmung} +\end{center} +\color{orange} Achtung: \color{black} +\begin{itemize*} + \item Aufwand für Berechnung einer Fibonaccizahl ist nicht konstant + \item Zufallszahlen als Eingabe +\end{itemize*} + +\paragraph{Diskussion: Speedup} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Speedup-Diskussion} +\end{center} + +\subsubsection{Datenparallelität: Das Map-Reduce-Paradigma} +\begin{itemize*} + \item Parallelisierungsmuster inspiriert von Konzepten funktionaler Programmiersprachen (map,reduce/fold) + \item Basis von Big-Data-plattformen wie Hadoop, Spark,... + \item Grundidee: \begin{itemize*} - \item Funktion F aufrufen, catch sorgt für korrekte Behandlung von Fehlern in F - \item Ergebnis zusammen mit eigener Pid (self()) an Elternprozess senden - \end{itemize*} - - \color{orange} Einsammeln der Ergebnisse \color{black} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-06} - \end{center} - \begin{itemize*} - \item Zeile 5: Warten bis Paar (Pid, Ergebniswert) eintrifft - \item Zeile 7: Tail ist leer $\rightarrow$ alle Ergebnisse eingetroffen - \end{itemize*} - - \paragraph{Parallele Berechnung der Fibonacci-Zahlen} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-07} - \end{center} - - \paragraph{Diskussion} - - \begin{itemize*} - \item Passende Abstraktion wählen + \item map(F, Seq) ? wende Funktion F (als Argument übergeben) auf alle Elemente einer Folge Seq an, \begin{itemize*} - \item Ist Ordnung der Ergebnisse notwendig? - \item Werden Ergebnisse benötigt? + \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden + \item Partitionieren und Verteilen der Elemente der Folge + \item z.B. multipliziere jedes Element mit 2 \end{itemize*} - \item Anzahl der parallelen Prozesse + \item reduce(F, Seq) = wende eine Funktion F schrittweise auf die Elemente einer Folge Seq an und produziere einen einzelnen Wert, \begin{itemize*} - \item Abhängig von Berechnungsmodell, Hardware etc. - \item evtl. pmap mit max. Anzahl gleichzeitiger Prozesse - \end{itemize*} - \item Berechnungsaufwand der Prozesse - \begin{itemize*} - \item Berechnung vs. Daten/Ergebnisse senden + \item prinzipiell ähnlich zu map(F, Seq), d.h. Funktion F kann auf Paare unabhängig angewendet werden + \item z.B. die Summe aller Elemente der Folge \end{itemize*} \end{itemize*} - - \paragraph{pmap: Alternative Implementierung} - +\end{itemize*} + +\paragraph{map in Erlang} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-map} +\end{center} + +\paragraph{reduce in Erlang} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-reduce} +\end{center} + +\paragraph{Parallelisierung von map und reduce} + +\color{orange} map \color{black} +\begin{itemize*} + \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden + \item Partitionieren und Verteilen der Elemente der Folge + \item siehe pmap +\end{itemize*} +\color{orange} reduce \color{black} +\begin{itemize*} + \item prinzipiell ähnlich, d.h. Funktion F kann auf Paare unabhängig angewandt werden +\end{itemize*} + +\paragraph{Parallelisierung von reduce} + +\begin{center} + \includegraphics[width=0.5\linewidth]{Assets/Programmierparadigmen-erlang-reduce-parallel} +\end{center} + +\subsubsection{Taskparallelität: Sortieren} +\color{orange} Quicksort in Erlang \color{black} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-11} +\end{center} + +\begin{itemize*} + \item typische funktionale Notation von Quicksort mit List Comprehensions + \item Zeile 2: H dient als Pivotelement +\end{itemize*} +Idee: +\begin{itemize*} + \item Prozess für das Sortieren der einen Hälfte starten + \item Elternprozess kann andere Hälfte sortieren + \item rekursive Zerlegung... +\end{itemize*} + +\subsubsection{Parallel Quicksort} +\paragraph{Version 1} + +Quicksort in Erlang +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-12} +\end{center} + +Erläuterungen +\begin{itemize*} + \item Zeile 4: Erzeugen eines neuen Prozesses zur Sortierung der "oberen" Hälfte + \item Zeile 6-7: Wie bisher + \item Zeile 8: Warten auf Empfang der sortierten anderen Hälfte +\end{itemize*} +Zeitmessung: +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-13} +\end{center} + +Bewertung +\begin{itemize*} + \item parallele Version 1 ist langsamer! + \item mögliche Erklärung: Prozess-Start ist aufwändiger als Sortieren kleiner Teilfolgen + \item bessere Variante nach John Hughes: Parallel Programming in Erlang \begin{itemize*} - \item ohne Berücksichtigung der Ordnung der Ergebnismenge - \item Zählen für die bereits eingetroffenen Ergebnisse + \item Kontrolle der Granularität für parallele Ausführungen + \item danach Sortieren mit sequenzieller Variante + \item einfache Idee: Anzahl der parallelen Zerlegung begrenzen \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-08} - \end{center} - - \subsubsection{Speedup} - Bestimmung des Speedups erfordert - \begin{itemize*} - \item Zeitmessung - \item Kontrolle der genutzten Prozessoren/Cores - \end{itemize*} - \color{orange} Welchen Einfluss hat die Zahl der erzeugten Prozesse. \color{black} - - \paragraph{Speedup: Zeitmessung} - - Nutzung der Funktion timer:tc/3 - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-09} - \end{center} - Für bessere Aussagekraft: mehrfache Ausführung - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-10} - \end{center} - - \paragraph{Bestimmung: Speedup} - - ch4\_6:benchmark(ch4\_4, run, 1000). - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Speedup-Bestimmung} - \end{center} - \color{orange} Achtung: \color{black} - \begin{itemize*} - \item Aufwand für Berechnung einer Fibonaccizahl ist nicht konstant - \item Zufallszahlen als Eingabe - \end{itemize*} - - \paragraph{Diskussion: Speedup} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Speedup-Diskussion} - \end{center} - - \subsubsection{Datenparallelität: Das Map-Reduce-Paradigma} - \begin{itemize*} - \item Parallelisierungsmuster inspiriert von Konzepten funktionaler Programmiersprachen (map,reduce/fold) - \item Basis von Big-Data-plattformen wie Hadoop, Spark,... - \item Grundidee: - \begin{itemize*} - \item map(F, Seq) ? wende Funktion F (als Argument übergeben) auf alle Elemente einer Folge Seq an, - \begin{itemize*} - \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden - \item Partitionieren und Verteilen der Elemente der Folge - \item z.B. multipliziere jedes Element mit 2 - \end{itemize*} - \item reduce(F, Seq) = wende eine Funktion F schrittweise auf die Elemente einer Folge Seq an und produziere einen einzelnen Wert, - \begin{itemize*} - \item prinzipiell ähnlich zu map(F, Seq), d.h. Funktion F kann auf Paare unabhängig angewendet werden - \item z.B. die Summe aller Elemente der Folge - \end{itemize*} - \end{itemize*} - \end{itemize*} - - \paragraph{map in Erlang} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-map} - \end{center} - - \paragraph{reduce in Erlang} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-reduce} - \end{center} - - \paragraph{Parallelisierung von map und reduce} - - \color{orange} map \color{black} - \begin{itemize*} - \item Funktion F kann unabhängig (=parallel) auf jedes Element angewendet werden - \item Partitionieren und Verteilen der Elemente der Folge - \item siehe pmap - \end{itemize*} - \color{orange} reduce \color{black} - \begin{itemize*} - \item prinzipiell ähnlich, d.h. Funktion F kann auf Paare unabhängig angewandt werden - \end{itemize*} - - \paragraph{Parallelisierung von reduce} - - \begin{center} - \includegraphics[width=0.5\linewidth]{Assets/Programmierparadigmen-erlang-reduce-parallel} - \end{center} - - \subsubsection{Taskparallelität: Sortieren} - \color{orange} Quicksort in Erlang \color{black} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-11} - \end{center} - - \begin{itemize*} - \item typische funktionale Notation von Quicksort mit List Comprehensions - \item Zeile 2: H dient als Pivotelement - \end{itemize*} - Idee: - \begin{itemize*} - \item Prozess für das Sortieren der einen Hälfte starten - \item Elternprozess kann andere Hälfte sortieren - \item rekursive Zerlegung... - \end{itemize*} - - \subsubsection{Parallel Quicksort} - \paragraph{Version 1} - - Quicksort in Erlang - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-12} - \end{center} - - Erläuterungen - \begin{itemize*} - \item Zeile 4: Erzeugen eines neuen Prozesses zur Sortierung der "oberen" Hälfte - \item Zeile 6-7: Wie bisher - \item Zeile 8: Warten auf Empfang der sortierten anderen Hälfte - \end{itemize*} - Zeitmessung: - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-13} - \end{center} - - Bewertung - \begin{itemize*} - \item parallele Version 1 ist langsamer! - \item mögliche Erklärung: Prozess-Start ist aufwändiger als Sortieren kleiner Teilfolgen - \item bessere Variante nach John Hughes: Parallel Programming in Erlang - \begin{itemize*} - \item Kontrolle der Granularität für parallele Ausführungen - \item danach Sortieren mit sequenzieller Variante - \item einfache Idee: Anzahl der parallelen Zerlegung begrenzen - \end{itemize*} - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-14} - \end{center} - - \paragraph{Version 2} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-15} - \end{center} - - \subsubsection{Fazit} - \begin{itemize*} - \item \color{orange} leichtgewichtige Prozesse \color{black} als Baustein der Parallelisierung in Erlang - \item Prozesskommunikation ausschließlich über \color{orange} Message Passing \color{black} - \item \color{orange} funktionaler Charakter \color{black} (u.a. Vermeidung von Seiteneffekten) vereinfacht Parallelisierung deutlich - \item \color{orange} Daten- und Taskparallelität \color{black} möglich - \item hoher Abstraktionsgrad, aber auch wenig Einflussmöglichkeiten - \end{itemize*} - - \subsection{Parallele Programmierung in C++} - \subsubsection{Threads in C++} - \color{orange} Thread (Faden) \color{black} = leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms - \begin{itemize*} - \item Threads teilen sich den Adressraum ihres Prozesses - \item in C++: Instanzen der Klasse std::thread - \item führen eine (initiale) Funktion aus - \end{itemize*} - \begin{lstlisting}[language=C++] +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-14} +\end{center} + +\paragraph{Version 2} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-15} +\end{center} + +\subsubsection{Fazit} +\begin{itemize*} + \item \color{orange} leichtgewichtige Prozesse \color{black} als Baustein der Parallelisierung in Erlang + \item Prozesskommunikation ausschließlich über \color{orange} Message Passing \color{black} + \item \color{orange} funktionaler Charakter \color{black} (u.a. Vermeidung von Seiteneffekten) vereinfacht Parallelisierung deutlich + \item \color{orange} Daten- und Taskparallelität \color{black} möglich + \item hoher Abstraktionsgrad, aber auch wenig Einflussmöglichkeiten +\end{itemize*} + +\subsection{Parallele Programmierung in C++} +\subsubsection{Threads in C++} +\color{orange} Thread (Faden) \color{black} = leichtgewichtige Ausführungseinheit oder Kontrollfluss (Folge von Anweisungen) innerhalb eines sich in Ausführung befindlichen Programms +\begin{itemize*} + \item Threads teilen sich den Adressraum ihres Prozesses + \item in C++: Instanzen der Klasse std::thread + \item führen eine (initiale) Funktion aus +\end{itemize*} +\begin{lstlisting}[language=C++] #include #include @@ -3569,16 +3569,16 @@ $\Rightarrow$ call-by-name, call-by-value t.join(); } \end{lstlisting} - - \paragraph{Alternative Erzeugung von Threads} - - \color{orange} über Lambda-Ausdruck \color{black} - \begin{lstlisting}[language=C++] + +\paragraph{Alternative Erzeugung von Threads} + +\color{orange} über Lambda-Ausdruck \color{black} +\begin{lstlisting}[language=C++] std::thread t([]() { do_something(); }); \end{lstlisting} - - \color{orange} mit Instanzen einer Klasse \color{black} - erfordert Überladen von operator() - \begin{lstlisting}[language=C++] + +\color{orange} mit Instanzen einer Klasse \color{black} - erfordert Überladen von operator() +\begin{lstlisting}[language=C++] struct my_task { void operator()() const { do_something(); } }; @@ -3587,14 +3587,14 @@ $\Rightarrow$ call-by-name, call-by-value std::thread t1(tsk); // mit Objekt std::thread t2{ my_task() }; // über Konstruktor \end{lstlisting} - - \paragraph{Parameterübergabe bei Threaderzeugung} - - \begin{itemize*} - \item über zusätzliche Argumente des thread-Konstruktors - \item Vorsicht bei Übergabe von Referenzen, wenn Elternthread vor dem erzeugten Thread beendet wird - \end{itemize*} - \begin{lstlisting}[language=C++] + +\paragraph{Parameterübergabe bei Threaderzeugung} + +\begin{itemize*} + \item über zusätzliche Argumente des thread-Konstruktors + \item Vorsicht bei Übergabe von Referenzen, wenn Elternthread vor dem erzeugten Thread beendet wird +\end{itemize*} +\begin{lstlisting}[language=C++] void fun(int n, const std::string\& s) { for (auto i = 0; i < n; i++) std::cout << s << " "; @@ -3603,38 +3603,38 @@ $\Rightarrow$ call-by-name, call-by-value std::thread t(fun, 2, "Hello"); t.join(); \end{lstlisting} - - \paragraph{Warten auf Threads} - - \begin{itemize*} - \item t.join() wartet auf Beendigung des Threads t - \item blockiert aktuellen Thread - \item ohne join() keine Garantie, dass t zur Ausführung kommt - \item Freigabe der Ressourcen des Threads - \end{itemize*} - \begin{lstlisting}[language=C++] + +\paragraph{Warten auf Threads} + +\begin{itemize*} + \item t.join() wartet auf Beendigung des Threads t + \item blockiert aktuellen Thread + \item ohne join() keine Garantie, dass t zur Ausführung kommt + \item Freigabe der Ressourcen des Threads +\end{itemize*} +\begin{lstlisting}[language=C++] std::thread t([]() { do_something(); }); t.join(); \end{lstlisting} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-16} - \end{center} - - \paragraph{Hintergrundthreads} - - \begin{itemize*} - \item Threads können auch im Hintergrund laufen, ohne, dass auf Ende gewartet werden muss - \item "abkoppeln" durch detach() - \item Thread läuft danach unter Kontrolle des C++-Laufzeitsystems, join nicht mehr möglich - \end{itemize*} - - \paragraph{Threadidentifikation} - - \begin{itemize*} - \item Threadidentifikator vom Typ std::thread::id - \item Ermittlung über Methode get\_id() - \end{itemize*} - \begin{lstlisting}[language=C++] +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-16} +\end{center} + +\paragraph{Hintergrundthreads} + +\begin{itemize*} + \item Threads können auch im Hintergrund laufen, ohne, dass auf Ende gewartet werden muss + \item "abkoppeln" durch detach() + \item Thread läuft danach unter Kontrolle des C++-Laufzeitsystems, join nicht mehr möglich +\end{itemize*} + +\paragraph{Threadidentifikation} + +\begin{itemize*} + \item Threadidentifikator vom Typ std::thread::id + \item Ermittlung über Methode get\_id() +\end{itemize*} +\begin{lstlisting}[language=C++] void fun() { std::cout << "Hello from " << std::this_thread::get_id() @@ -3643,140 +3643,140 @@ $\Rightarrow$ call-by-name, call-by-value std::thread t(fun); t.join(); \end{lstlisting} - - \paragraph{Beispiel: Berechnung von Fibonacci-Zahlen in C++} - + +\paragraph{Beispiel: Berechnung von Fibonacci-Zahlen in C++} + +\begin{itemize*} + \item rekursive und nichtrekursive Variante möglich +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-17} +\end{center} + +Parallele Berechnung von Fibonacci-Zahlen +\begin{itemize*} + \item einfachste Lösung (ähnlich zu Erlang): pro Zahl ein Thread +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-18} +\end{center} + +Erläuterungen +\begin{itemize*} + \item Zeile 1: Feld der Threads + \item Zeile 2: Feld für Ergebniswerte + \item Zeile 5: Zufallszahl erzeugen + \item Zeilen 6-7 Thread zur Berechnung der Fibonacci-Zahl erzeugen und Ergebnis im Feld speichern + \item Zeile 10-11: Warten auf Beendigung der Threads (std::mem\_fn = Wrapper für Zeiger auf Member-Funktion) + \item \color{orange} aber: \color{black} \begin{itemize*} - \item rekursive und nichtrekursive Variante möglich + \item Zugriff auf gemeinsame Ressource (results)! + \item Anzahl Fibonaccizahlen = Anzahl Threads \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-17} - \end{center} - - Parallele Berechnung von Fibonacci-Zahlen - \begin{itemize*} - \item einfachste Lösung (ähnlich zu Erlang): pro Zahl ein Thread - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-18} - \end{center} - - Erläuterungen - \begin{itemize*} - \item Zeile 1: Feld der Threads - \item Zeile 2: Feld für Ergebniswerte - \item Zeile 5: Zufallszahl erzeugen - \item Zeilen 6-7 Thread zur Berechnung der Fibonacci-Zahl erzeugen und Ergebnis im Feld speichern - \item Zeile 10-11: Warten auf Beendigung der Threads (std::mem\_fn = Wrapper für Zeiger auf Member-Funktion) - \item \color{orange} aber: \color{black} - \begin{itemize*} - \item Zugriff auf gemeinsame Ressource (results)! - \item Anzahl Fibonaccizahlen = Anzahl Threads - \end{itemize*} - \end{itemize*} - - \paragraph{parallel-for in C++} - - \begin{itemize*} - \item Unterstützung durch Higher-Level-APIs und Frameworks - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-19} - \end{center} - - \paragraph{Kontrolle der Anzahl der Threads} - - \begin{itemize*} - \item Erzeugung von Threads ist mit Kosten verbunden - \item begrenzte Anzahl von Hardwarethreads (Anzahl Cores, Hyperthreading) - \item Ermittlung der Anzahl der unterstützten Hardwarethreads - \end{itemize*} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-20} - \end{center} - \begin{itemize*} - \item Nutzung für Implementierung von Threadpools, Task Libraries, ... - \end{itemize*} - - \subsubsection{Probleme nebenläufiger Ausführung} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-21} - \end{center} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-22} - \end{center} - Ausgabe: - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-23} - \end{center} - \color{orange} Race Conditions \color{black}(Wettlaufsituationen) := Ergebnis nebenläufiger Ausführung auf gemeinsamen Zustand (hier: Ausgabekanal) hängt vom zeitlichen Verhalten der Einzeloperationen ab - - - \begin{itemize*} - \item Race Conditions (Wettlaufsituation) := Ergebnis nebenläufiger Ausführung auf gemeinsamen Zustand (hier: Ausgabekanal) hängt vom zeitlichen Verhalten der Einzeloperationen ab - \item kritischer Abschnitt: Programmabschnitt in einem Thread, in dem auf eine gemeinsame Ressource (Speicher etc.) zugegriffen wird und der nicht parallel (oder zeitlich verzahnt) zu einem anderen Thread ausgeführt werden darf - \item Lösung durch wechselseitigen Ausschluss (engl. mutual exclusion = mutex) - \begin{itemize*} - \item Instanz der Klasse std::mutex - \item Methoden zum Sperren ( lock ) und Freigeben ( unlock ) - \item 'mutex' : Standard-Mutex für exklusiven Zugriff - \item 'timed\_mutex' : Mutex mit Timeout für Warten ( try\_lock\_for() ) - \item 'recursive\_mutex' : rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe - \item 'recursive\_timed\_mutex' : rekursives Mutex mit Timeout - \item 'shared\_mutex' : Mutex, das gemeinsamen Zugriff ( lock\_shared() ) mehrerer Threads oder exklusiven Zugriff ( lock() ) ermöglicht - \item 'shared\_timed\_mutex' : Mutex mit Timeout und gemeinsamen Zugriff - \end{itemize*} - \item Lock Guards - \begin{itemize*} - \item Vereinfachung der Nutzung von Mutexen durch RAII ("Ressourcenbelegung ist Initialisierung") - \item Konstruktor = lock - \item Destruktor = unlock - \item std::unique\_lock erweiterte Variante von lock\_guard, vermeidet aber sofortiges Sperren - \item std::lock : erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen - \item Sperrstrategien: u.a. - \begin{itemize*} - \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen - \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt - \end{itemize*} - \end{itemize*} - \end{itemize*} - - \subsubsection{Wechselseitiger Ausschluss} - \begin{itemize*} - \item \color{orange} kritischer Abschnitt\color{black}: Programmabschnitt in einem Thread, in dem auf eine gemeinsame Ressource (Speicher etc.) zugegriffen wird und der nicht parallel (oder zeitlich verzahnt) zu einem anderen Thread ausgeführt werden darf - \item Lösung durch \color{orange} wechselseitigen Ausschluss \color{black} (engl. mutual exclusion = mutex) - \end{itemize*} - - \paragraph{Mutex in C++} - +\end{itemize*} + +\paragraph{parallel-for in C++} + +\begin{itemize*} + \item Unterstützung durch Higher-Level-APIs und Frameworks +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-19} +\end{center} + +\paragraph{Kontrolle der Anzahl der Threads} + +\begin{itemize*} + \item Erzeugung von Threads ist mit Kosten verbunden + \item begrenzte Anzahl von Hardwarethreads (Anzahl Cores, Hyperthreading) + \item Ermittlung der Anzahl der unterstützten Hardwarethreads +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-20} +\end{center} +\begin{itemize*} + \item Nutzung für Implementierung von Threadpools, Task Libraries, ... +\end{itemize*} + +\subsubsection{Probleme nebenläufiger Ausführung} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-21} +\end{center} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-22} +\end{center} +Ausgabe: +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-23} +\end{center} +\color{orange} Race Conditions \color{black}(Wettlaufsituationen) := Ergebnis nebenläufiger Ausführung auf gemeinsamen Zustand (hier: Ausgabekanal) hängt vom zeitlichen Verhalten der Einzeloperationen ab + + +\begin{itemize*} + \item Race Conditions (Wettlaufsituation) := Ergebnis nebenläufiger Ausführung auf gemeinsamen Zustand (hier: Ausgabekanal) hängt vom zeitlichen Verhalten der Einzeloperationen ab + \item kritischer Abschnitt: Programmabschnitt in einem Thread, in dem auf eine gemeinsame Ressource (Speicher etc.) zugegriffen wird und der nicht parallel (oder zeitlich verzahnt) zu einem anderen Thread ausgeführt werden darf + \item Lösung durch wechselseitigen Ausschluss (engl. mutual exclusion = mutex) \begin{itemize*} \item Instanz der Klasse std::mutex - \item Methoden zum Sperren (lock) und Freigeben (unlock) + \item Methoden zum Sperren ( lock ) und Freigeben ( unlock ) + \item 'mutex' : Standard-Mutex für exklusiven Zugriff + \item 'timed\_mutex' : Mutex mit Timeout für Warten ( try\_lock\_for() ) + \item 'recursive\_mutex' : rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe + \item 'recursive\_timed\_mutex' : rekursives Mutex mit Timeout + \item 'shared\_mutex' : Mutex, das gemeinsamen Zugriff ( lock\_shared() ) mehrerer Threads oder exklusiven Zugriff ( lock() ) ermöglicht + \item 'shared\_timed\_mutex' : Mutex mit Timeout und gemeinsamen Zugriff \end{itemize*} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-mutex-c} - \end{center} - - \paragraph{Mutex-Varianten} - - \begin{itemize*} - \item mutex: Standard-Mutex für exklusiven Zugriff - \item timed\_mutex: Mutex mit Timeout für Warten (try\_lock\_for()) - \item recursive\_mutex:rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe - \item recursive\_timed\_mutex: rekursives Mutex mit Timeout - \item shared\_mutex: Mutex, das gemeinsamen Zugriff (lock\_shared()) mehrerer Threads oder exklusiven Zugriff (lock()) ermöglicht - \item shared\_timed\_mutex: Mutex mit Timeout und gemeinsamen Zugriff - \end{itemize*} - - \paragraph{Lock Guards} - + \item Lock Guards \begin{itemize*} \item Vereinfachung der Nutzung von Mutexen durch RAII ("Ressourcenbelegung ist Initialisierung") \item Konstruktor = lock \item Destruktor = unlock + \item std::unique\_lock erweiterte Variante von lock\_guard, vermeidet aber sofortiges Sperren + \item std::lock : erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen + \item Sperrstrategien: u.a. + \begin{itemize*} + \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen + \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt + \end{itemize*} \end{itemize*} - - \begin{lstlisting}[language=C++] +\end{itemize*} + +\subsubsection{Wechselseitiger Ausschluss} +\begin{itemize*} + \item \color{orange} kritischer Abschnitt\color{black}: Programmabschnitt in einem Thread, in dem auf eine gemeinsame Ressource (Speicher etc.) zugegriffen wird und der nicht parallel (oder zeitlich verzahnt) zu einem anderen Thread ausgeführt werden darf + \item Lösung durch \color{orange} wechselseitigen Ausschluss \color{black} (engl. mutual exclusion = mutex) +\end{itemize*} + +\paragraph{Mutex in C++} + +\begin{itemize*} + \item Instanz der Klasse std::mutex + \item Methoden zum Sperren (lock) und Freigeben (unlock) +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-mutex-c} +\end{center} + +\paragraph{Mutex-Varianten} + +\begin{itemize*} + \item mutex: Standard-Mutex für exklusiven Zugriff + \item timed\_mutex: Mutex mit Timeout für Warten (try\_lock\_for()) + \item recursive\_mutex:rekursives Mutex - erlaubt mehrfaches Sperren durch einen Thread, z.B. für rekursive Aufrufe + \item recursive\_timed\_mutex: rekursives Mutex mit Timeout + \item shared\_mutex: Mutex, das gemeinsamen Zugriff (lock\_shared()) mehrerer Threads oder exklusiven Zugriff (lock()) ermöglicht + \item shared\_timed\_mutex: Mutex mit Timeout und gemeinsamen Zugriff +\end{itemize*} + +\paragraph{Lock Guards} + +\begin{itemize*} + \item Vereinfachung der Nutzung von Mutexen durch RAII ("Ressourcenbelegung ist Initialisierung") + \item Konstruktor = lock + \item Destruktor = unlock +\end{itemize*} + +\begin{lstlisting}[language=C++] std::vector data; std::mutex my_mtx; @@ -3790,326 +3790,326 @@ $\Rightarrow$ call-by-name, call-by-value return data.front(); } \end{lstlisting} - - \paragraph{Lock Gurads und Locks} - + +\paragraph{Lock Gurads und Locks} + +\begin{itemize*} + \item std::unique\_lock erweiterte Variante von lock\_guards, vermeidet aber sofortiges Sperren + \item std::lock erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen + \item Sperrstrategien: u.a. \begin{itemize*} - \item std::unique\_lock erweiterte Variante von lock\_guards, vermeidet aber sofortiges Sperren - \item std::lock erlaubt gleichzeitiges deadlock-freies Sperren von 2 Mutexen - \item Sperrstrategien: u.a. - \begin{itemize*} - \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen - \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt - \end{itemize*} + \item std::try\_to\_lock versucht Sperre ohne Blockierung zu setzen + \item std::adopt\_lock versucht nicht, ein zweites Mal zu sperren, wenn bereits durch den aktuellen Thread gesperrt \end{itemize*} - - \paragraph{Atomare Datentypen} - +\end{itemize*} + +\paragraph{Atomare Datentypen} + +\begin{itemize*} + \item std::atomic\_flag = sperrfreier, atomarer Datentyp: \begin{itemize*} - \item std::atomic\_flag = sperrfreier, atomarer Datentyp: - \begin{itemize*} - \item clear() setzt den Wert auf false - \item test\_and\_set() setzt den Wert atomar auf true und liefert den vorherigen Wert - \end{itemize*} - \item std::atomic = mächtigere Variante, erlaubt explizites Setzen - \begin{itemize*} - \item operator= atomare Wertzuweisung - \item load() liefert den aktuellen Wert - \item read-modify-write-Operation (siehe später) - \end{itemize*} - \item std::atomic = generische Variante für weitere Datentypen + \item clear() setzt den Wert auf false + \item test\_and\_set() setzt den Wert atomar auf true und liefert den vorherigen Wert \end{itemize*} - - - \paragraph{Synchronisation über atomare Variable} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-24} - \end{center} - Erläuterungen + \item std::atomic = mächtigere Variante, erlaubt explizites Setzen \begin{itemize*} - \item Zeile 1: gemeinsam genutzte Liste - erfordert synchronisierten Zugriff - \item Zeile 2: atomare boolsche Variable ready - \item Zeile 4/12: Konsument/Produzent-Threads - \item Zeile 5: atomares prüfen der ready-Variablen - \item Zeile 6-7 kurz warten und neu versuchen - \item Zeile 8-9/13 Zugriff auf gemeinsame Liste - \item Zeile 14: atomares Setzen der Variablen ready + \item operator= atomare Wertzuweisung + \item load() liefert den aktuellen Wert + \item read-modify-write-Operation (siehe später) \end{itemize*} - - \subsubsection{Taskparallelität: Die 5 speisenden Philosophen} - \begin{itemize*} - \item fünf Philosophen teilen sich eine Schüssel Sphagetti - \item fünf Gabeln, je eine zwischen zwei Philosophen - \item Philosoph kann nur mit zwei benachbarten Gabeln essen - \item Gabeln werden nur nach dem Essen zurückgelegt - \item Philosoph durchläuft Zyklus von Zuständen: denken $\rightarrow$ hungrig $\rightarrow$ essen $\rightarrow$ denken $\rightarrow$ etc. - \end{itemize*} - - \paragraph{Das Problem mit den Philosophen} - - \begin{itemize*} - \item Jeder greift die linke Gabel - \item und wartet auf die rechte Gabel - \item ... und wartet ... - \end{itemize*} - \begin{center} - \includegraphics[width=0.3\linewidth]{Assets/Programmierparadigmen-philosophen} - \end{center} - \color{orange} \textbf{Verklemmung!} \color{black} - - \paragraph{Lösungsidee} - - \begin{itemize*} - \item immer beide Gabeln aufnehmen, dh. wenn nur eine Gabel verfügbar ist: liegen lassen und warten - \item synchronisierter Zugriff auf Gablen, dh. in einem kritischen Abschnitt unter gegenseitige Ausschluss - \item Wecken von wartenden Philosophen - \end{itemize*} - - \paragraph{Verklemmungsfreies Sperren} - - \begin{lstlisting}[language=C++] + \item std::atomic = generische Variante für weitere Datentypen +\end{itemize*} + + +\paragraph{Synchronisation über atomare Variable} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-24} +\end{center} +Erläuterungen +\begin{itemize*} + \item Zeile 1: gemeinsam genutzte Liste - erfordert synchronisierten Zugriff + \item Zeile 2: atomare boolsche Variable ready + \item Zeile 4/12: Konsument/Produzent-Threads + \item Zeile 5: atomares prüfen der ready-Variablen + \item Zeile 6-7 kurz warten und neu versuchen + \item Zeile 8-9/13 Zugriff auf gemeinsame Liste + \item Zeile 14: atomares Setzen der Variablen ready +\end{itemize*} + +\subsubsection{Taskparallelität: Die 5 speisenden Philosophen} +\begin{itemize*} + \item fünf Philosophen teilen sich eine Schüssel Sphagetti + \item fünf Gabeln, je eine zwischen zwei Philosophen + \item Philosoph kann nur mit zwei benachbarten Gabeln essen + \item Gabeln werden nur nach dem Essen zurückgelegt + \item Philosoph durchläuft Zyklus von Zuständen: denken $\rightarrow$ hungrig $\rightarrow$ essen $\rightarrow$ denken $\rightarrow$ etc. +\end{itemize*} + +\paragraph{Das Problem mit den Philosophen} + +\begin{itemize*} + \item Jeder greift die linke Gabel + \item und wartet auf die rechte Gabel + \item ... und wartet ... +\end{itemize*} +\begin{center} + \includegraphics[width=0.3\linewidth]{Assets/Programmierparadigmen-philosophen} +\end{center} +\color{orange} \textbf{Verklemmung!} \color{black} + +\paragraph{Lösungsidee} + +\begin{itemize*} + \item immer beide Gabeln aufnehmen, dh. wenn nur eine Gabel verfügbar ist: liegen lassen und warten + \item synchronisierter Zugriff auf Gablen, dh. in einem kritischen Abschnitt unter gegenseitige Ausschluss + \item Wecken von wartenden Philosophen +\end{itemize*} + +\paragraph{Verklemmungsfreies Sperren} + +\begin{lstlisting}[language=C++] std::lock(mtx1, mtx2); std::lock_guard lk1(mtx1, std::adopt_lock); std::lock_guard lk2(mtx2, std::adopt_lock); \end{lstlisting} - Führt zu Verklemmung; Alternative Lösung - \begin{lstlisting}[language=C++] +Führt zu Verklemmung; Alternative Lösung +\begin{lstlisting}[language=C++] std::unique_lock lk1(mtx1, std::defer_lock); std::unique_lock lk2(mtx2, std::defer_lock); std::lock(lk1, lk2); \end{lstlisting} - - \paragraph{Gabeln und Spaghetti-Teller} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-25} - \end{center} - - \paragraph{Die Philosophen-Klasse} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-26} - \end{center} - - \paragraph{Die Philosophen-Klasse: Hilfsmethoden} - - Textausgabe erfordert synchronisierten Zugriff auf cout über globalen Mutex - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-27} - \end{center} - Hilfsmethode für zufällige Wartezeit in Millisekunden - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-28} - \end{center} - - \paragraph{Die Philosophen-Klasse: Essen} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-29} - \end{center} - - \paragraph{Die Philosophen-Klasse: Denken} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-30} - \end{center} - - \paragraph{Das Leben eines Philosophen} - +\paragraph{Gabeln und Spaghetti-Teller} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-25} +\end{center} + +\paragraph{Die Philosophen-Klasse} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-26} +\end{center} + +\paragraph{Die Philosophen-Klasse: Hilfsmethoden} + +Textausgabe erfordert synchronisierten Zugriff auf cout über globalen Mutex +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-27} +\end{center} + +Hilfsmethode für zufällige Wartezeit in Millisekunden +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-28} +\end{center} + +\paragraph{Die Philosophen-Klasse: Essen} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-29} +\end{center} + +\paragraph{Die Philosophen-Klasse: Denken} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-30} +\end{center} + +\paragraph{Das Leben eines Philosophen} + +\begin{itemize*} + \item Zur Erinnerung: überladener ()-Operator eines Objekts definiert auszuführende Funktion eines Threads +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-31} +\end{center} + +\paragraph{Das Dinner: Initialisierung} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-32} +\end{center} + +\paragraph{Das Dinner beginnt} + +\begin{itemize*} + \item Beginn (und Ende) des Dinners über atomare Variable signalisieren + \item Philosophen-Threads arbeiten ihre operator()()-Methode ab +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-33} +\end{center} + +\paragraph{Fazit} + +\begin{itemize*} + \item Philosophenproblem: klassisches Problem der Informatik zur Demonstration von Nebenläufigkeit und Verklemmung + \item von Edsger W. Dijkstra formuliert + \item betrachte C++ Lösung illustriert \begin{itemize*} - \item Zur Erinnerung: überladener ()-Operator eines Objekts definiert auszuführende Funktion eines Threads + \item Nebenläufigkeit durch Threads + \item Synchronisation über Mutexe + \item verklemmungsfreies Sperren \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-31} - \end{center} - - \paragraph{Das Dinner: Initialisierung} - - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-32} - \end{center} - - \paragraph{Das Dinner beginnt} - + \item moderene C++ Sprachversion vereinfacht Programmierung gegenüber Low-Level-API auf Betriebssystemebene (z.B. pthreads) +\end{itemize*} + +\subsubsection{Weitere Möglichkeiten der Thread-Interaktion} +\begin{itemize*} + \item bisher: \begin{itemize*} - \item Beginn (und Ende) des Dinners über atomare Variable signalisieren - \item Philosophen-Threads arbeiten ihre operator()()-Methode ab + \item Mutexe und Locks + \item atomare Variablen \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-33} - \end{center} - - \paragraph{Fazit} - + \item typischer Anwendungsfall: Warten auf Ereignis / Setzen eines Flags +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-34} +\end{center} + +\subsubsection{Bedingungsvariablen} +\begin{itemize*} + \item Thread wartet, bis Bedingung erfüllt ist + \item Erfüllung der Bedingung wird durch anderen Thread angezeigt (notify) $\rightarrowtail$ "Aufwecken" des wartenden Threads + \item notwendig: synchronisierter Zugriff über Mutex +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-35} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-36} +\end{center} + +\subsubsection{Thread-sichere Datenstrukturen} +\begin{itemize*} + \item Thread-Sicherheit := eine Komponente kann gleichzeitig von verschiedenen Programmbereichen (Threads) mehrfach ausgeführt werden, ohne dass diese sich gegenseitig behindern + \item verschiedene Varianten: \begin{itemize*} - \item Philosophenproblem: klassisches Problem der Informatik zur Demonstration von Nebenläufigkeit und Verklemmung - \item von Edsger W. Dijkstra formuliert - \item betrachte C++ Lösung illustriert - \begin{itemize*} - \item Nebenläufigkeit durch Threads - \item Synchronisation über Mutexe - \item verklemmungsfreies Sperren - \end{itemize*} - \item moderene C++ Sprachversion vereinfacht Programmierung gegenüber Low-Level-API auf Betriebssystemebene (z.B. pthreads) + \item Standard-Datenstruktur + über Mutexe/Sperren synchronisierte Zugriffe + \item Integration der Sperren in die Datenstruktur + \item Sperr-freie Datenstrukturen: nicht-blockierend, Vermeidung von Sperren, z.B. durch Compare/Exchange-Operationen \end{itemize*} - - \subsubsection{Weitere Möglichkeiten der Thread-Interaktion} - \begin{itemize*} - \item bisher: - \begin{itemize*} - \item Mutexe und Locks - \item atomare Variablen - \end{itemize*} - \item typischer Anwendungsfall: Warten auf Ereignis / Setzen eines Flags - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-34} - \end{center} - - \subsubsection{Bedingungsvariablen} - \begin{itemize*} - \item Thread wartet, bis Bedingung erfüllt ist - \item Erfüllung der Bedingung wird durch anderen Thread angezeigt (notify) $\rightarrowtail$ "Aufwecken" des wartenden Threads - \item notwendig: synchronisierter Zugriff über Mutex - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-35} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-36} - \end{center} - - \subsubsection{Thread-sichere Datenstrukturen} - \begin{itemize*} - \item Thread-Sicherheit := eine Komponente kann gleichzeitig von verschiedenen Programmbereichen (Threads) mehrfach ausgeführt werden, ohne dass diese sich gegenseitig behindern - \item verschiedene Varianten: - \begin{itemize*} - \item Standard-Datenstruktur + über Mutexe/Sperren synchronisierte Zugriffe - \item Integration der Sperren in die Datenstruktur - \item Sperr-freie Datenstrukturen: nicht-blockierend, Vermeidung von Sperren, z.B. durch Compare/Exchange-Operationen - \end{itemize*} - \item async , future und promise - \item std::future - Resultat einer asynchronen Berechnung, d.h. einer Berechnung die erst noch stattfindet - \item std::async() - asynchrones Starten eines Tasks - \begin{lstlisting}[language=C++] + \item async , future und promise + \item std::future - Resultat einer asynchronen Berechnung, d.h. einer Berechnung die erst noch stattfindet + \item std::async() - asynchrones Starten eines Tasks + \begin{lstlisting}[language=C++] int long_calculation() { ... } std::future result = std::async(long_calculation); // Fortsetzung der Berechnung ... result.wait(); std::cout << result.get() << std::endl; \end{lstlisting} - \item std::promise - erlaubt Wert zu setzen, wenn der aktuelle Thread beendet ist; oft in Kombination mit std::future eingesetzt - \item future = Ergebnisobjekt, promise = Ergebnisproduzent - \begin{itemize*} - \item Warten auf Ende des Tasks (wait(), wait\_for()) - \item Ergebnis lesen (get()) - \end{itemize*} - \end{itemize*} - - \subsubsection{Anforderungen} + \item std::promise - erlaubt Wert zu setzen, wenn der aktuelle Thread beendet ist; oft in Kombination mit std::future eingesetzt + \item future = Ergebnisobjekt, promise = Ergebnisproduzent \begin{itemize*} - \item mehrere Threads können gleichzeitig auf die Datenstruktur zugreifen - \item kein Thread sieht (Zwischen-)Zustand, bei dem Invarianten der Datenstruktur durch einen anderen Thread (kurzzeitig) verletzt ist - \item Vermeidung von Wettlaufsituationen - \item Vermeidung von Verklemmungen - \item korrekte Behandlung von Ausnahmen (Fehlern) + \item Warten auf Ende des Tasks (wait(), wait\_for()) + \item Ergebnis lesen (get()) \end{itemize*} - - \subsubsection{Thread-sichere Queue} +\end{itemize*} + +\subsubsection{Anforderungen} +\begin{itemize*} + \item mehrere Threads können gleichzeitig auf die Datenstruktur zugreifen + \item kein Thread sieht (Zwischen-)Zustand, bei dem Invarianten der Datenstruktur durch einen anderen Thread (kurzzeitig) verletzt ist + \item Vermeidung von Wettlaufsituationen + \item Vermeidung von Verklemmungen + \item korrekte Behandlung von Ausnahmen (Fehlern) +\end{itemize*} + +\subsubsection{Thread-sichere Queue} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-37} +\end{center} +\begin{itemize*} + \item Zeilen 1,2,6: Kapselung der std::queue-Klasse + \item Zeile 4: Mutex für exklusiven Zugriff + \item Zeile 5: Bedingungsvariable für Warten +\end{itemize*} + +\paragraph{Thread-sichere Queue: Methode push} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-38} +\end{center} +\begin{itemize*} + \item Zeile 2: Lock Guard sichert exklusiven Zugriff + \item Zeile 3: Element an die Queue anhängen + \item Zeile 4: Aufwecken von eventuell wartenden Threads +\end{itemize*} + +\paragraph{Thread-sichere Queue: Methode waiting\_pop} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-39} +\end{center} +\begin{itemize*} + \item Zeile 2: Lock Guard sichert exklusiven Zugriff + \item Zeile 3: Warten bis Queue nicht mehr leer ist + \item Zeilen 4,5: erstes Element aus der Queue entnehmen +\end{itemize*} + +\subsubsection{async, future und promise} +\begin{itemize*} + \item std::future - Resultat einer asynchronen Berechnung, d.h. einer Berechnung die erst noch stattfindet + \item std::async() - asynchrones Starten eines Tasks \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-37} + \centering + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-40} \end{center} + \item std::promise - erlaubt Wert zu setzen, wenn der aktuelle Thread beendet ist, of in Kombination mit std::future eingesetzt + \item future = Ergbenisobjekt, promise = Ergebnisproduzent +\end{itemize*} + +\paragraph{Future} + +\begin{itemize*} + \item Methoden zum \begin{itemize*} - \item Zeilen 1,2,6: Kapselung der std::queue-Klasse - \item Zeile 4: Mutex für exklusiven Zugriff - \item Zeile 5: Bedingungsvariable für Warten + \item Warten auf das Ende des Tasks (wait(), wait\_for()) + \item Ergebnis lesen (get()) \end{itemize*} - - \paragraph{Thread-sichere Queue: Methode push} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-38} - \end{center} +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-future-task} +\end{center} + +Beispiel +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-41} +\end{center} + +\subsubsection{Deklarative Parallelisierung mit OpenMP} +\begin{itemize*} + \item Programmierschnittstelle für Parallelisierung in C/C++/Fortran + \item Programmiersprachenerweiterung durch Direktiven + \item in C/C++: \#pragma omp ... + \item zusätzliche Bibliotheksfunktionen: \#include + \item aktuelle Version 5.0 + \item Unterstützung in gcc und clang \begin{itemize*} - \item Zeile 2: Lock Guard sichert exklusiven Zugriff - \item Zeile 3: Element an die Queue anhängen - \item Zeile 4: Aufwecken von eventuell wartenden Threads + \item vollständig 4.5, partiell 5.0 + \item Nutzung über Compilerflag - fopenmp \end{itemize*} - - \paragraph{Thread-sichere Queue: Methode waiting\_pop} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-39} - \end{center} - \begin{itemize*} - \item Zeile 2: Lock Guard sichert exklusiven Zugriff - \item Zeile 3: Warten bis Queue nicht mehr leer ist - \item Zeilen 4,5: erstes Element aus der Queue entnehmen - \end{itemize*} - - \subsubsection{async, future und promise} - \begin{itemize*} - \item std::future - Resultat einer asynchronen Berechnung, d.h. einer Berechnung die erst noch stattfindet - \item std::async() - asynchrones Starten eines Tasks - \begin{center} - \centering - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-40} - \end{center} - \item std::promise - erlaubt Wert zu setzen, wenn der aktuelle Thread beendet ist, of in Kombination mit std::future eingesetzt - \item future = Ergbenisobjekt, promise = Ergebnisproduzent - \end{itemize*} - - \paragraph{Future} - - \begin{itemize*} - \item Methoden zum - \begin{itemize*} - \item Warten auf das Ende des Tasks (wait(), wait\_for()) - \item Ergebnis lesen (get()) - \end{itemize*} - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-future-task} - \end{center} - - Beispiel - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-41} - \end{center} - - \subsubsection{Deklarative Parallelisierung mit OpenMP} - \begin{itemize*} - \item Programmierschnittstelle für Parallelisierung in C/C++/Fortran - \item Programmiersprachenerweiterung durch Direktiven - \item in C/C++: \#pragma omp ... - \item zusätzliche Bibliotheksfunktionen: \#include - \item aktuelle Version 5.0 - \item Unterstützung in gcc und clang - \begin{itemize*} - \item vollständig 4.5, partiell 5.0 - \item Nutzung über Compilerflag - fopenmp - \end{itemize*} - \item beschränkt auf Architekturen mit gemeinsamen Speicher - \end{itemize*} - - \subsubsection{Programmiermodell} - \begin{itemize*} - \item Master-Thread und mehrere Worker-Threads (Anzahl typischerweise durch OpenMP-Laufzeitsystem bestimmt) - \item über parallel-Direktive kann Arbeit in einem Programmabschnitt auf Worker-Threads aufgeteilt werden - \item Ende des parallelen Abschnitts $\rightarrowtail$ implizite Synchronisation - \item Fortsetzung des Master-Threads - \end{itemize*} - \begin{center} - \includegraphics[width=0.2\linewidth]{Assets/Programmierparadigmen-master-worker-thread} - \end{center} - - - \begin{itemize*} - \item Master-Thread und mehrere Worker-Threads (Anzahl typischerweise durch OpenMP-Laufzeitsystem bestimmt) - \item über parallel -Direktive kann Arbeit in einem Programmabschnitt auf Worker-Threads aufgeteilt werden - \item Ende des parallelen Abschnitts $\rightarrow$ implizite Synchronisation $\rightarrow$ Fortsetzung des Master-Threads - \item der dem 'pragma' folgende Block wird parallel von allen Threads ausgeführt - \begin{lstlisting}[language=C++] + \item beschränkt auf Architekturen mit gemeinsamen Speicher +\end{itemize*} + +\subsubsection{Programmiermodell} +\begin{itemize*} + \item Master-Thread und mehrere Worker-Threads (Anzahl typischerweise durch OpenMP-Laufzeitsystem bestimmt) + \item über parallel-Direktive kann Arbeit in einem Programmabschnitt auf Worker-Threads aufgeteilt werden + \item Ende des parallelen Abschnitts $\rightarrowtail$ implizite Synchronisation + \item Fortsetzung des Master-Threads +\end{itemize*} +\begin{center} + \includegraphics[width=0.2\linewidth]{Assets/Programmierparadigmen-master-worker-thread} +\end{center} + + +\begin{itemize*} + \item Master-Thread und mehrere Worker-Threads (Anzahl typischerweise durch OpenMP-Laufzeitsystem bestimmt) + \item über parallel -Direktive kann Arbeit in einem Programmabschnitt auf Worker-Threads aufgeteilt werden + \item Ende des parallelen Abschnitts $\rightarrow$ implizite Synchronisation $\rightarrow$ Fortsetzung des Master-Threads + \item der dem 'pragma' folgende Block wird parallel von allen Threads ausgeführt + \begin{lstlisting}[language=C++] #include #include int main() { @@ -4123,52 +4123,52 @@ $\Rightarrow$ call-by-name, call-by-value return 0; } \end{lstlisting} - \item Schleifenparallelisierung: jedem Thread wird ein Teil der Iteration zugewiesen (beeinflusst nur äußere Schleife) - \begin{lstlisting}[language=C++] + \item Schleifenparallelisierung: jedem Thread wird ein Teil der Iteration zugewiesen (beeinflusst nur äußere Schleife) + \begin{lstlisting}[language=C++] ... #pragma omp parallel for for (int i = 0; i < 20; i++) {... \end{lstlisting} - \begin{itemize*} - \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst und auf die Threads verteilt werden sollen - \begin{lstlisting} + \begin{itemize*} + \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst und auf die Threads verteilt werden sollen + \begin{lstlisting} #pragma omp parallel for collapse(3) \end{lstlisting} - \end{itemize*} - \item Beeinflussung der Thread Anzahl - \begin{itemize*} - \item maximale Anzahl: + \end{itemize*} + \item Beeinflussung der Thread Anzahl + \begin{itemize*} + \item maximale Anzahl: \begin{lstlisting} #pragma omp parallel for num_threads(8) \end{lstlisting} - \item bedingte Parallelisierung: + \item bedingte Parallelisierung: \begin{lstlisting} #pragma omp parallel for if(i>50) \end{lstlisting} - \end{itemize*} - \item Aufteilung des Iterationsbereichs; Beeinflussung durch schedule -Direktive - \begin{itemize*} - \item schedule(auto): Default - implementierungsspezifisch - \item schedule(static, n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) - \item schedule(dynamic, n): dynamische Verteilung nach Bedarf - \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit - \end{itemize*} - \item Direktiven für parallele Ausführung - \begin{itemize*} - \item '\#pragma omp single/master' Abschnitt wird nur durch einen/den Master-Thread ausgeführt - \item '\#pragma omp critical' kritischer Abschnitt - \item '\#pragma omp barrier' Warten auf alle Worker-Threads - \item '\#pragma omp atomic' kritischer Abschnitt, Zugriff auf gemeinsame Variable (z.B. Zähler) - \end{itemize*} - \item Speicherklauseln für Variablen - \begin{itemize*} - \item 'shared' für alle Threads sichtbar/änderbar - \item 'private' jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert - \item 'reduction' private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden - \item 'firstprivate / lastprivate' privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben - \end{itemize*} - \item zuweisung von Programmabschnitten zu Threads $\rightarrow$ statische Parallelität (geeignet für rekursive Abschnitte) - \begin{lstlisting}[language=C++] + \end{itemize*} + \item Aufteilung des Iterationsbereichs; Beeinflussung durch schedule -Direktive + \begin{itemize*} + \item schedule(auto): Default - implementierungsspezifisch + \item schedule(static, n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) + \item schedule(dynamic, n): dynamische Verteilung nach Bedarf + \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit + \end{itemize*} + \item Direktiven für parallele Ausführung + \begin{itemize*} + \item '\#pragma omp single/master' Abschnitt wird nur durch einen/den Master-Thread ausgeführt + \item '\#pragma omp critical' kritischer Abschnitt + \item '\#pragma omp barrier' Warten auf alle Worker-Threads + \item '\#pragma omp atomic' kritischer Abschnitt, Zugriff auf gemeinsame Variable (z.B. Zähler) + \end{itemize*} + \item Speicherklauseln für Variablen + \begin{itemize*} + \item 'shared' für alle Threads sichtbar/änderbar + \item 'private' jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert + \item 'reduction' private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden + \item 'firstprivate / lastprivate' privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben + \end{itemize*} + \item zuweisung von Programmabschnitten zu Threads $\rightarrow$ statische Parallelität (geeignet für rekursive Abschnitte) + \begin{lstlisting}[language=C++] #pragma omp parallel sections { #pragma omp section @@ -4177,13 +4177,13 @@ $\Rightarrow$ call-by-name, call-by-value qsort(data, p + 1, right); } \end{lstlisting} - \item Task Programmierung - \begin{itemize*} - \item reihum Threads zugewiesen werden - \item an beliebiger Stelle definiert werden können - \item von beliebigem Thread definiert werden kann - \end{itemize*} - \begin{lstlisting}[language=C++] + \item Task Programmierung + \begin{itemize*} + \item reihum Threads zugewiesen werden + \item an beliebiger Stelle definiert werden können + \item von beliebigem Thread definiert werden kann + \end{itemize*} + \begin{lstlisting}[language=C++] unsigned int f1, f2; #pragma omp task shared(f1) f1 = fib(f - 1); @@ -4192,111 +4192,111 @@ $\Rightarrow$ call-by-name, call-by-value #pragma omp taskwait return f1 + f2; \end{lstlisting} - \end{itemize*} - - \subsubsection{Hello World! mit OpenMP} - \begin{itemize*} - \item der dem pragma folgende Block wird parallel von allen Threads ausgeführt - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-42} - \end{center} - - \subsubsection{Schleifenparallelisierung} - \begin{itemize*} - \item parallele Ausführung einer Schleife: jedem Thread wird ein Teil der Iterationen zugewiesen - \item für for-Schleifgen mit eingeschränkter Syntax (ganzzahlige Schleifenvariablen, Operatoren auf Schleifenvariablen) und für STL-Iteratoren - \end{itemize*} - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-43} - \end{center} +\end{itemize*} - \subsubsection{Beeinflussung der Thread-Anzahl} - maximale Anzahl - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-44} - \end{center} - \noindent - bedingte Parallelisierung - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-45} - \end{center} - - \subsubsection{Aufteilung des Iterationsbereichs} +\subsubsection{Hello World! mit OpenMP} +\begin{itemize*} + \item der dem pragma folgende Block wird parallel von allen Threads ausgeführt +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-42} +\end{center} + +\subsubsection{Schleifenparallelisierung} +\begin{itemize*} + \item parallele Ausführung einer Schleife: jedem Thread wird ein Teil der Iterationen zugewiesen + \item für for-Schleifgen mit eingeschränkter Syntax (ganzzahlige Schleifenvariablen, Operatoren auf Schleifenvariablen) und für STL-Iteratoren +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-43} +\end{center} + +\subsubsection{Beeinflussung der Thread-Anzahl} +maximale Anzahl +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-44} +\end{center} +\noindent +bedingte Parallelisierung +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-45} +\end{center} + +\subsubsection{Aufteilung des Iterationsbereichs} +\begin{itemize*} + \item Iterationsbereich kann auf verschiedene Weise auf Threads aufgeteilt werden + \item Beeinflussung durch schedule-Direktive \begin{itemize*} - \item Iterationsbereich kann auf verschiedene Weise auf Threads aufgeteilt werden - \item Beeinflussung durch schedule-Direktive - \begin{itemize*} - \item schedule(auto): Default - implementierungsspezifisch - \item schedule(static,n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) - \item schedule(dynamic, n): dynamische Verteilung nach Bedarf - \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit - \item ... - \end{itemize*} + \item schedule(auto): Default - implementierungsspezifisch + \item schedule(static,n): statische Round-Robin-Verteilung - Bereiche der Größe n (Angabe von n ist optional) + \item schedule(dynamic, n): dynamische Verteilung nach Bedarf + \item schedule(guided, n): Verteilung nach Bedarf und proportional zur Restarbeit + \item ... \end{itemize*} - - \subsubsection{Geschachtelte Schleifen} +\end{itemize*} + +\subsubsection{Geschachtelte Schleifen} +\begin{itemize*} + \item Parallelisierung mit \textbf{parallel for} beeinflusst nur äußere Schleife + \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst, und auf die Threads verteilt werden sollen + \item Beispiel: Matrizenmultiplikation +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-46} +\end{center} + +\subsubsection{Synchronisation} +\begin{itemize*} + \item Direktiven für parallele Ausführung \begin{itemize*} - \item Parallelisierung mit \textbf{parallel for} beeinflusst nur äußere Schleife - \item collapse(n) gibt an, dass n Schleifen in einem gemeinsamen Iterationsbereich zusammengefasst, und auf die Threads verteilt werden sollen - \item Beispiel: Matrizenmultiplikation + \item \textbf{\#pragma omp single/master} Abschnitt wird nur durch einen/den Master-Thread ausgeführt + \item \textbf{\#pragma omp critical} kritischer Abschnitt + \item \textbf{\#pragma omp barrier} Warten auf alle Worker-Threads + \item \textbf{\#pragma omp atomic} kritischer Abschnitt - ZUgriff auf gemeinsame Variable (z.B. Zähler) \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-46} - \end{center} - - \subsubsection{Synchronisation} + \item Speicherklauseln für Variablen \begin{itemize*} - \item Direktiven für parallele Ausführung - \begin{itemize*} - \item \textbf{\#pragma omp single/master} Abschnitt wird nur durch einen/den Master-Thread ausgeführt - \item \textbf{\#pragma omp critical} kritischer Abschnitt - \item \textbf{\#pragma omp barrier} Warten auf alle Worker-Threads - \item \textbf{\#pragma omp atomic} kritischer Abschnitt - ZUgriff auf gemeinsame Variable (z.B. Zähler) - \end{itemize*} - \item Speicherklauseln für Variablen - \begin{itemize*} - \item \textbf{shared} für alle Threads sichtbar/änderbar - \item \textbf{private} jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert - \item \textbf{reduction} private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden - \item \textbf{firstprivate/lastprivate} privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben - \end{itemize*} + \item \textbf{shared} für alle Threads sichtbar/änderbar + \item \textbf{private} jeder Thread hat eigene Kopie der Daten, wird nicht außerhalb initialisiert + \item \textbf{reduction} private Daten, die am Ende des Abschnitts zu globalem Wert zusammengefasst werden + \item \textbf{firstprivate/lastprivate} privat - initialisiert mit letztem Wert vor dem Abschnitt / Wert des letzten Threads der Iteration wird zurückgegeben \end{itemize*} - - \subsubsection{Parallele Abschnitte} +\end{itemize*} + +\subsubsection{Parallele Abschnitte} +\begin{itemize*} + \item Zuweisung von Programmabschnitten zu Threads $\rightarrowtail$ statische Parallelität + \item geeignet z.B. für rekursive Aufrufe +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-47} +\end{center} + +\subsubsection{Task-Programmierung mit OpenMP} +\begin{itemize*} + \item seit OpenMP 3.0 Unterstützung von Tasks, die \begin{itemize*} - \item Zuweisung von Programmabschnitten zu Threads $\rightarrowtail$ statische Parallelität - \item geeignet z.B. für rekursive Aufrufe + \item reihum Threads zugewiesen werden + \item an beliebiger Stelle definiert werden können + \item von beliebigem Thread definiert werden kann \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-47} - \end{center} - - \subsubsection{Task-Programmierung mit OpenMP} +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-48} +\end{center} + +\subsubsection{Fazit} +\begin{itemize*} + \item C++ bietet weitreichende und mächtige Konzepte zur Parallelisierung \begin{itemize*} - \item seit OpenMP 3.0 Unterstützung von Tasks, die - \begin{itemize*} - \item reihum Threads zugewiesen werden - \item an beliebiger Stelle definiert werden können - \item von beliebigem Thread definiert werden kann - \end{itemize*} - \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-48} - \end{center} - - \subsubsection{Fazit} - \begin{itemize*} - \item C++ bietet weitreichende und mächtige Konzepte zur Parallelisierung - \begin{itemize*} - \item von Basiskontrolle wie Threads und Synchronisationsprimitiven (u.a. Mutexe) - \item ...über höherwertige Abstraktionen wie async, Features und Promises - \item bis hin zu deklarativen Ansätzen wie OpenMP - \end{itemize*} - \item alle Formen von Parallelität (Instruktions-, Daten-, und Taskparallelität) möglich - \item aber anspruchsvolle Programmierung - \item erleichtert durch zusätzliche Bibliotheken und Frameworks wie Parallel STL, TBB, ... + \item von Basiskontrolle wie Threads und Synchronisationsprimitiven (u.a. Mutexe) + \item ...über höherwertige Abstraktionen wie async, Features und Promises + \item bis hin zu deklarativen Ansätzen wie OpenMP \end{itemize*} + \item alle Formen von Parallelität (Instruktions-, Daten-, und Taskparallelität) möglich + \item aber anspruchsvolle Programmierung + \item erleichtert durch zusätzliche Bibliotheken und Frameworks wie Parallel STL, TBB, ... +\end{itemize*} \begin{lstlisting}[language=java] //[Hello.java] @@ -4390,42 +4390,42 @@ countdown(Start) $\rightarrow$ countdown() $\rightarrow$ countdown(10). \end{lstlisting} - - - \subsection{Parallele Programmierung in Java} - Unterstützung durch + + +\subsection{Parallele Programmierung in Java} +Unterstützung durch +\begin{itemize*} + \item Thread-Konzept + \item eingebaute Mechanismen zur Synchronisation nebenläufiger Prozesse + \item spezielle High-Level-Klassen im Package + \newline \textbf{java.util.concurrent} +\end{itemize*} + +\subsubsection{Threads in Java} +\begin{itemize*} + \item Repräsentiert durch Klasse \textbf{java.lang.Thread} + \item Implementierung eines eigenen Kontrollflusses + \item Eigene Klasse muss Runnable implementieren \begin{itemize*} - \item Thread-Konzept - \item eingebaute Mechanismen zur Synchronisation nebenläufiger Prozesse - \item spezielle High-Level-Klassen im Package - \newline \textbf{java.util.concurrent} - \end{itemize*} - - \subsubsection{Threads in Java} - \begin{itemize*} - \item Repräsentiert durch Klasse \textbf{java.lang.Thread} - \item Implementierung eines eigenen Kontrollflusses - \item Eigene Klasse muss Runnable implementieren + \item Implementierung des Interface \textbf{java.lang.Runnable} \begin{itemize*} - \item Implementierung des Interface \textbf{java.lang.Runnable} - \begin{itemize*} - \item keine weitere Beeinflussung des Threads über zusätzliche Methoden notwendig - \item soll von anderer Klasse als Thread abgeleitet werden - \end{itemize*} - \item Subklasse von \textbf{java.lang.Thread} - \begin{itemize*} - \item zusätzliche Methoden zur Steuerung des Ablaufs benötigt - \item keine andere Superklasse notwendig - \end{itemize*} + \item keine weitere Beeinflussung des Threads über zusätzliche Methoden notwendig + \item soll von anderer Klasse als Thread abgeleitet werden + \end{itemize*} + \item Subklasse von \textbf{java.lang.Thread} + \begin{itemize*} + \item zusätzliche Methoden zur Steuerung des Ablaufs benötigt + \item keine andere Superklasse notwendig \end{itemize*} \end{itemize*} - - \paragraph{Threads: Runnable-Schnittstelle} - Eigene Klasse muss \textbf{Runnable} implementieren - \begin{itemize*} - \item Methode \textbf{public void run()} - wird beim Start des Threads aufgerufen - \end{itemize*} - \begin{lstlisting}[language=java] +\end{itemize*} + +\paragraph{Threads: Runnable-Schnittstelle} +Eigene Klasse muss \textbf{Runnable} implementieren +\begin{itemize*} + \item Methode \textbf{public void run()} - wird beim Start des Threads aufgerufen +\end{itemize*} +\begin{lstlisting}[language=java] public class Heartbeat implements Runnable { int pulse; public Heartbeat(int p) { pulse = p * 1000; } @@ -4439,135 +4439,135 @@ countdown() $\rightarrow$ } \end{lstlisting} - \paragraph{Thread-Erzeugung} +\paragraph{Thread-Erzeugung} +\begin{itemize*} + \item Thread-Objekt mit Runnable-Objekt erzeugen + \item Methode \textbf{start()} aufrufen \begin{itemize*} - \item Thread-Objekt mit Runnable-Objekt erzeugen - \item Methode \textbf{start()} aufrufen - \begin{itemize*} - \item Ruft \textbf{run()} auf - \end{itemize*} + \item Ruft \textbf{run()} auf \end{itemize*} - \begin{lstlisting}[language=java] +\end{itemize*} +\begin{lstlisting}[language=java] public static void main(String[] args) { Thread t = new Thread(new Heartbeat(2)); //Thread Objekt mit runnable erzeugen t.start(); //methode start() aufrufen $\rightarrow$ ruft run() auf } \end{lstlisting} - - \paragraph{Subklasse von Thread} - + +\paragraph{Subklasse von Thread} + +\begin{itemize*} + \item Klasse muss von Thread abgeleitet werden + \item Methode run() muss überschrieben werden +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-49} +\end{center} + +\begin{itemize*} + \item Objekt der eigenen Thread-Klasse erzeugen + \item Methode \textbf{start()} aufrufen \begin{itemize*} - \item Klasse muss von Thread abgeleitet werden - \item Methode run() muss überschrieben werden + \item Ruft \textbf{run()} auf \end{itemize*} \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-49} + \centering + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-50} \end{center} - + \item Spätere Beeinflussung durch andere Threads möglich + \begin{center} + \centering + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-51} + \end{center} +\end{itemize*} + + +\paragraph{Threads: Wichtige Methoden} + +\begin{description*} + \item[void start()] initiiert Ausführung des Threads durch Aufruf der Methode run + \item[void run()] die eigentliche Arbeitsmethode + \item[static void sleep(int millis)] hält die Ausführung des aktuellen Threads für 'millis' Millisekunden an; Keinen Einfluss auf andere Threads! + \item[void join()] blockiert den aufrufenden Thread so lange, bis der aufgerufene Thread beendet ist +\end{description*} + +\subsubsection{Parallele Berechnung von Fibonacci-Zahlen} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-52} +\end{center} +Thread-Erzeugung und Ausführung +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-53} +\end{center} + +\subsubsection{Wechselseitiger Ausschluss in Java} +Schlüsselwort \textbf{synchronized} +\begin{itemize*} + \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren) \begin{itemize*} - \item Objekt der eigenen Thread-Klasse erzeugen - \item Methode \textbf{start()} aufrufen - \begin{itemize*} - \item Ruft \textbf{run()} auf - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-50} - \end{center} - \item Spätere Beeinflussung durch andere Threads möglich - \begin{center} - \centering - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-51} - \end{center} + \item nur ein Thread darf den kritischen Abschnitt betreten + \item alle anderen Threads, die darauf zugrifen wollen, müssen auf Freigabe warten \end{itemize*} - - - \paragraph{Threads: Wichtige Methoden} - - \begin{description*} - \item[void start()] initiiert Ausführung des Threads durch Aufruf der Methode run - \item[void run()] die eigentliche Arbeitsmethode - \item[static void sleep(int millis)] hält die Ausführung des aktuellen Threads für 'millis' Millisekunden an; Keinen Einfluss auf andere Threads! - \item[void join()] blockiert den aufrufenden Thread so lange, bis der aufgerufene Thread beendet ist - \end{description*} - - \subsubsection{Parallele Berechnung von Fibonacci-Zahlen} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-52} - \end{center} - Thread-Erzeugung und Ausführung - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-53} - \end{center} - - \subsubsection{Wechselseitiger Ausschluss in Java} - Schlüsselwort \textbf{synchronized} + \item für Methoden: \textbf{public synchronized void doSomething()} \begin{itemize*} - \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren) - \begin{itemize*} - \item nur ein Thread darf den kritischen Abschnitt betreten - \item alle anderen Threads, die darauf zugrifen wollen, müssen auf Freigabe warten - \end{itemize*} - \item für Methoden: \textbf{public synchronized void doSomething()} + \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen + \end{itemize*} + \item für Anweisungen: \textbf{synchronized(anObject)\{...\}} + \begin{itemize*} + \item nur ein Thread darf den Block betreten + \item Sperre wird durch das Objekt \textbf{anObject} verwaltet (jedem Java-Objekt ist eine Sperre zugeordnet) + \end{itemize*} +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-java-synchronized} +\end{center} +\begin{itemize*} + \item Schlüsselwort synchronized + \begin{itemize*} + \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren); nur ein Thread darf den kritischen Abschnitt betreten; alle anderen Threads, die darauf zugreifen wollen, müssen auf Freigabe warten + \item für Methoden: + \begin{lstlisting} + public synchronized void doSomething() + \end{lstlisting} \begin{itemize*} \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen \end{itemize*} - \item für Anweisungen: \textbf{synchronized(anObject)\{...\}} + \item für Anweisungen: synchronized(anObject) { ... } \begin{itemize*} \item nur ein Thread darf den Block betreten - \item Sperre wird durch das Objekt \textbf{anObject} verwaltet (jedem Java-Objekt ist eine Sperre zugeordnet) + \item Sperre wird durch das Objekt anObject verwaltet (jedem Java-Objekt ist ein Sperre zugeordnet) \end{itemize*} \end{itemize*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-java-synchronized} - \end{center} +\end{itemize*} + +\subsubsection{wait \& notify} +\begin{description*} + \item[] Signalisierung zwischen Threads in Java + \item[] Basismethoden der Klasse \textbf{java.lang.Object} + \item[wait()] der aktive Thread wartet an diesem Objekt, Sperren werden ggf. freigegeben. + \item[notify()] wekct an diesem Objekt wartenden Thread auf + \item[notifyAll()] weckt alle an diesem Objekt wartenden Threads auf + \item[wait() \& notify()] dürfen nur in einem \textbf{synchronized}-Block aufgerufen werden +\end{description*} + +\subsubsection{Java: High-Level-Klassen} +\begin{itemize*} + \item Paket \textbf{java.util.concurrent} seit Java Version 1.5 + \item Abstraktionsschicht versteckt Details über Thread-Erzeugung + \item Übernimmt Erstellung und Überwachung von parallelen Tasks, u.a. \begin{itemize*} - \item Schlüsselwort synchronized - \begin{itemize*} - \item Implementierung von sogenannten Monitoren bzw. locks (exklusiven Sperren); nur ein Thread darf den kritischen Abschnitt betreten; alle anderen Threads, die darauf zugreifen wollen, müssen auf Freigabe warten - \item für Methoden: - \begin{lstlisting} - public synchronized void doSomething() - \end{lstlisting} - \begin{itemize*} - \item nur ein Thread darf diese Methode auf einem Objekt zur gleichen Zeit ausführen - \end{itemize*} - \item für Anweisungen: synchronized(anObject) { ... } - \begin{itemize*} - \item nur ein Thread darf den Block betreten - \item Sperre wird durch das Objekt anObject verwaltet (jedem Java-Objekt ist ein Sperre zugeordnet) - \end{itemize*} - \end{itemize*} - \end{itemize*} - - \subsubsection{wait \& notify} - \begin{description*} - \item[] Signalisierung zwischen Threads in Java - \item[] Basismethoden der Klasse \textbf{java.lang.Object} - \item[wait()] der aktive Thread wartet an diesem Objekt, Sperren werden ggf. freigegeben. - \item[notify()] wekct an diesem Objekt wartenden Thread auf - \item[notifyAll()] weckt alle an diesem Objekt wartenden Threads auf - \item[wait() \& notify()] dürfen nur in einem \textbf{synchronized}-Block aufgerufen werden - \end{description*} - - \subsubsection{Java: High-Level-Klassen} - \begin{itemize*} - \item Paket \textbf{java.util.concurrent} seit Java Version 1.5 - \item Abstraktionsschicht versteckt Details über Thread-Erzeugung - \item Übernimmt Erstellung und Überwachung von parallelen Tasks, u.a. - \begin{itemize*} - \item \textbf{ExecutorService} zum erzeugen asynchroner Tasks - \item \textbf{Future}: Referenz auf diesen Task bzw. dessen Ergebnis - \item \textbf{ForkJoinPool \& RecursiveAction}: rekursives Aufteilen eines großen Problems - \end{itemize*} - \end{itemize*} - - \subsubsection{Tasks und Futures in Java} - \begin{itemize*} - \item Task = logische Ausführungseinheit - \item Thread = Mechanismus zur asynchronen/parallelen Ausführung von Tasks + \item \textbf{ExecutorService} zum erzeugen asynchroner Tasks + \item \textbf{Future}: Referenz auf diesen Task bzw. dessen Ergebnis + \item \textbf{ForkJoinPool \& RecursiveAction}: rekursives Aufteilen eines großen Problems \end{itemize*} +\end{itemize*} + +\subsubsection{Tasks und Futures in Java} +\begin{itemize*} + \item Task = logische Ausführungseinheit + \item Thread = Mechanismus zur asynchronen/parallelen Ausführung von Tasks +\end{itemize*} \begin{lstlisting}[language=java] Runnable task = () -> { @@ -4578,341 +4578,272 @@ task.run(); Thread thread = new Thread(task); thread.start(); \end{lstlisting} - - \subsubsection{Future \& ExecutorService} + +\subsubsection{Future \& ExecutorService} +\begin{itemize*} + \item \textbf{ExecutorService} stellt Methoden zum Starten/Beenden/Steuern von parallelen Aufgaben bereit + \item implementiert \textbf{Executor}-Interface \begin{itemize*} - \item \textbf{ExecutorService} stellt Methoden zum Starten/Beenden/Steuern von parallelen Aufgaben bereit - \item implementiert \textbf{Executor}-Interface + \item definiert Methode \textbf{void execute(Runnable r)} + \end{itemize*} + \item Starten einer Aufgabe mit \textbf{submit} + \begin{itemize*} + \item \textbf{$Future$ submit(Callable c)} + \item \textbf{$Future$ submit(Runnable r)} + \end{itemize*} + \item Zugriff auf das Ergebnis mit \textbf{get} + \begin{itemize*} + \item \textbf{T get(long timeout, TimeUnit unit)} + \item \textbf{T get()} + \end{itemize*} +\end{itemize*} + +\paragraph{Future \& ExecutorService: Beispiel} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-54} +\end{center} + +\subsubsection{RecursiveAction \& Fork/Join} +\begin{itemize*} + \item Rekursives Zerlegen eines großen Problems in kleinere Probleme + \item Solange bis Problem klein genug um direkt ausgeführt werden zu können + \item Task erstellt zwei oder mehr Teiltasks von sich selbst $\rightarrowtail$ Datenparallelität + \item ForkJoinPool zum Ausführen $\rightarrow$ implementiert Executor Interface + \item Fazit + \begin{itemize*} + \item Parallelprogrammierung in Java sehr ähnlich zu C++ + \item Konzepte: Threads, kritische Abschnitte über synchronized + \item mächtige Abstraktionen in java.util.concurrent \begin{itemize*} - \item definiert Methode \textbf{void execute(Runnable r)} - \end{itemize*} - \item Starten einer Aufgabe mit \textbf{submit} - \begin{itemize*} - \item \textbf{$Future$ submit(Callable c)} - \item \textbf{$Future$ submit(Runnable r)} - \end{itemize*} - \item Zugriff auf das Ergebnis mit \textbf{get} - \begin{itemize*} - \item \textbf{T get(long timeout, TimeUnit unit)} - \item \textbf{T get()} + \item Tasks und Futures, Executor und ThreadPool + \item thread-sichere Datenstrukturen + \item Synchronisation: Barrieren, Semaphoren, ... \end{itemize*} \end{itemize*} - - \paragraph{Future \& ExecutorService: Beispiel} - - \begin{center} - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-code-snippet-54} - \end{center} - - \subsubsection{RecursiveAction \& Fork/Join} +\end{itemize*} + +\paragraph{Beispiel} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-55} +\end{center} +Starten der Verarbeitung: +\begin{enumerate*} + \item (große) Gesamtaufgabe erstellen + \item ForkJoinPool erstellen + \item Aufgabe vom Pool ausführen lassen +\end{enumerate*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-56} +\end{center} + +\subsection{Zusammenfassung} +\begin{itemize*} + \item Parallelprogrammierung als wichtige Technik zur Nutzung moderner Hardware (Multicore, GPU, ...) + \item verschiedene Architekturen und Programmiermodelle + \item Instruktions-, Daten- und Taskparallelität + \item Message Passing vs. gemeinsamer Speicher + \item Konzepte in Erlang, C++, Java + \item hoher Abstraktionsgrad funktionaler Sprachen + \item C++/Java: Thread-Modell und Synchronisation mit vielen weiteren Konzepten + \item höherwertige Abstraktion durch zusätzliche Bibliotheken und Programmierschnittstellen +\end{itemize*} + +\newpage +\section{Verteilte Programmierung} + +\subsection{Grundlagen} +\subsubsection{Lernziele} +\begin{itemize*} + \item Verständnis von Techniken verteilter Programmierung als Paradigma \begin{itemize*} - \item Rekursives Zerlegen eines großen Problems in kleinere Probleme - \item Solange bis Problem klein genug um direkt ausgeführt werden zu können - \item Task erstellt zwei oder mehr Teiltasks von sich selbst $\rightarrowtail$ Datenparallelität - \item ForkJoinPool zum Ausführen $\rightarrow$ implementiert Executor Interface - \item Fazit - \begin{itemize*} - \item Parallelprogrammierung in Java sehr ähnlich zu C++ - \item Konzepte: Threads, kritische Abschnitte über synchronized - \item mächtige Abstraktionen in java.util.concurrent - \begin{itemize*} - \item Tasks und Futures, Executor und ThreadPool - \item thread-sichere Datenstrukturen - \item Synchronisation: Barrieren, Semaphoren, ... - \end{itemize*} - \end{itemize*} + \item Modelle und Konzepte unabhängig von Programmiersprache und Betriebssystem + \item Herausforderungen und Besonderheiten verteilter Programme \end{itemize*} - - \paragraph{Beispiel} - - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-55} - \end{center} - Starten der Verarbeitung: - \begin{enumerate*} - \item (große) Gesamtaufgabe erstellen - \item ForkJoinPool erstellen - \item Aufgabe vom Pool ausführen lassen - \end{enumerate*} - \begin{center} - \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-56} - \end{center} - - \subsection{Zusammenfassung} + \item Kennenlernen konkreter Konzepte und Mechanismen \begin{itemize*} - \item Parallelprogrammierung als wichtige Technik zur Nutzung moderner Hardware (Multicore, GPU, ...) - \item verschiedene Architekturen und Programmiermodelle - \item Instruktions-, Daten- und Taskparallelität - \item Message Passing vs. gemeinsamer Speicher - \item Konzepte in Erlang, C++, Java - \item hoher Abstraktionsgrad funktionaler Sprachen - \item C++/Java: Thread-Modell und Synchronisation mit vielen weiteren Konzepten - \item höherwertige Abstraktion durch zusätzliche Bibliotheken und Programmierschnittstellen + \item praktische Beispiele in Java, Erlang und C++ + \item Bewertung und Vergleich verschiedener Plattformen \end{itemize*} - - \newpage - \section{Verteilte Programmierung} - - \paragraph{Aktormodell} - +\end{itemize*} + +\subsubsection{Einordnung} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-einordnung-programmierung} +\end{center} +\begin{itemize*} + \item mehrere Rechner + \item Prozesse auf verschiedenen Rechnern + \item Kommunikation über Knotengrenzen hinweg + \item Behandlung von Knoten- oder Netzwerkausfällen +\end{itemize*} + +\subsubsection{Ziele} +\begin{itemize*} + \item Bisher: \begin{itemize*} - \item formales Modell für Nebenläufigkeit und Verteilung - \item Basis für verschiedene Programmiersprachen/Frameworks: Erlang, Akka (Scala/Java) - \item Prinzipien: - \begin{itemize*} - \item Aktor kapselt Zustand und Verhalten - \item Aktoren sind aktiv - \item Aktoren kommunizieren durch Nachrichtenaustausch - \begin{itemize*} - \item Nichtblockierendes Senden - \item Blockierendes Empfangen - \end{itemize*} - \end{itemize*} + \item eine Maschine + \item Prozesse kommunizieren nur innerhalb dieser Maschine (shared Memory vs. Message Passing) \end{itemize*} - - Aktormodell in Erlang - \begin{itemize*} - \item Aktormodell in Erlang nativ umgesetzt - \begin{itemize*} - \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt - \item bisher aber nur auf einem Knoten - \end{itemize*} - \item Programmbestandteile im Aktormodell - \begin{itemize*} - \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ ... end. - \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spawn(fun ... ) . - \item Empfangen von Nachrichten $\Rightarrow$ receive ... end . - \item Senden $\Rightarrow$ Pid ! Request . - \end{itemize*} - \item kein globaler Zustand - \end{itemize*} - - Cookie-System - \begin{itemize*} - \item erteilte Erlang-Knoten benötigen zur Kommunikation gemeinsames Magic Cookie (Passwort) - \item Mehrere Varianten - \begin{itemize*} - \item Datei \~/.erlang.cookie - \item Erlang-Funktion - \begin{lstlisting}[language=erlang] - :set_cookie(node(), Cookie). - \end{lstlisting} - \item Option - \begin{lstlisting} - erl -setcookie Cookie - \end{lstlisting} - \end{itemize*} - \item Verbindungsaufbau mittels Funktion - \begin{lstlisting} - net_adm:ping(adress) - \end{lstlisting} - \end{itemize*} - - Das Alternating Bit Protokoll - Übersicht - \begin{itemize*} - \item ermöglicht es, Nachrichten über einen verlustbehafteten Kommunikationskanal vollständig zu übertragen, sofern Verluste nur gelegentlich auftreten (transiente Fehler) - \item Empfänger quittiert jedes erhaltene Paket (Ackknowledgement, kurz: Ack) - \item Achtung: Kanal kann Nachrichten und Acks verlieren - \begin{itemize*} - \item $\Rightarrow$ benötigen je zwei unterschiedliche Sequenznummern und Acks - \end{itemize*} - \item Empfänger liefert eine Nachricht nur beim ersten Empfang aus (keine Duplikate) - \item bei Timeout: Nachricht erneut senden - \item bei Erhalt eines unerwarteten Ack: aktuelle Nachricht erneut senden - \end{itemize*} - - \subsection{Grundlagen} - \subsubsection{Lernziele} - \begin{itemize*} - \item Verständnis von Techniken verteilter Programmierung als Paradigma - \begin{itemize*} - \item Modelle und Konzepte unabhängig von Programmiersprache und Betriebssystem - \item Herausforderungen und Besonderheiten verteilter Programme - \end{itemize*} - \item Kennenlernen konkreter Konzepte und Mechanismen - \begin{itemize*} - \item praktische Beispiele in Java, Erlang und C++ - \item Bewertung und Vergleich verschiedener Plattformen - \end{itemize*} - \end{itemize*} - - \subsubsection{Einordnung} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-001} - \end{center} + \item Jetzt: \begin{itemize*} \item mehrere Rechner \item Prozesse auf verschiedenen Rechnern + \end{itemize*} + \item Erfordert: + \begin{itemize*} \item Kommunikation über Knotengrenzen hinweg \item Behandlung von Knoten- oder Netzwerkausfällen \end{itemize*} - - \subsubsection{Ziele} +\end{itemize*} + +\subsubsection{Motivation} +\begin{itemize*} + \item viele verschiedene Systeme (Knoten) zur Verfügung \begin{itemize*} - \item Bisher: - \begin{itemize*} - \item eine Maschine - \item Prozesse kommunizieren nur innerhalb dieser Maschine (shared Memory vs. Message Passing) - \end{itemize*} - \item Jetzt: - \begin{itemize*} - \item mehrere Rechner - \item Prozesse auf verschiedenen Rechnern - \end{itemize*} - \item Erfordert: - \begin{itemize*} - \item Kommunikation über Knotengrenzen hinweg - \item Behandlung von Knoten- oder Netzwerkausfällen - \end{itemize*} + \item PC, Server, virtuelle Maschinen + \item Lastverteilung, Spezialisierung auf bestimmte Probleme \end{itemize*} - - \subsubsection{Motivation} + \item Knoten sind über Netzwerke verbunden \begin{itemize*} - \item viele verschiedene Systeme (Knoten) zur Verfügung - \begin{itemize*} - \item PC, Server, virtuelle Maschinen - \item Lastverteilung, Spezialisierung auf bestimmte Probleme - \end{itemize*} - \item Knoten sind über Netzwerke verbunden - \begin{itemize*} - \item LAN(Wohnräume, Büros,...): bis zu 10 Gbit/s - \item MAN(Metropolitan Area Network, Behördennetze, dicht besiedelte Regionen): bis zu 10 Gbit/s - \item WAN(Wide Area Network, weltweite Vernetzung): hohe Kapazitäten zwischen den ISPs - \end{itemize*} + \item LAN(Wohnräume, Büros,...): bis zu 10 Gbit/s + \item MAN(Metropolitan Area Network, Behördennetze, dicht besiedelte Regionen): bis zu 10 Gbit/s + \item WAN(Wide Area Network, weltweite Vernetzung): hohe Kapazitäten zwischen den ISPs \end{itemize*} - - Wofür werden verteilte Systeme eingesetzt? +\end{itemize*} + +Wofür werden verteilte Systeme eingesetzt? +\begin{itemize*} + \item Gemeinsame Nutzung von Ressourcen \begin{itemize*} - \item Gemeinsame Nutzung von Ressourcen - \begin{itemize*} - \item Cloud-Umgebungen - \item verteilte Datenbanksysteme - \end{itemize*} - \item Teilaufgaben in großen Anwendungen - \begin{itemize*} - \item parallele Ausführung - \item getrennte Teilaufgaben (Micro-Services) - \end{itemize*} - \item Informationsaustausch - \begin{itemize*} - \item Email, Messenger - \item verteilte Algorithmen - \end{itemize*} + \item Cloud-Umgebungen + \item verteilte Datenbanksysteme \end{itemize*} - - \subsubsection{Software Architekturen} - \begin{enumerate*} - \item Früher: Hardware, Betriebssystem, Anwendung - \begin{itemize*} - \item Virtualisierung von Prozessor, Speicher, E/A Systemen - \item Interprozesskommunikation (IPC) - \end{itemize*} - \item Middlewaresysteme: Hardware, OS, Middleware, Anwendung - \begin{itemize*} - \item verteilte Dienste - \item Programmierparadigmen: RPC, Client/Server,... - \item Java, CORBA, ... - \end{itemize*} - \item Heute: Virtualisierung - \begin{itemize*} - \item VM Hypervisor: verstecken Hardware vor dem Betriebssystem - \item Docker: eine Anwendung pro Container - \end{itemize*} - \end{enumerate*} - - \subsubsection{Herausforderungen} + \item Teilaufgaben in großen Anwendungen \begin{itemize*} - \item viele verschiedene Computer/Server - \begin{itemize*} - \item verschiedene Betriebssysteme - \item unterschiedliche Leistungsfähigkeit - \end{itemize*} - \item Systemkomponenten müssen miteinander kommunizieren - \item verteilte Algorithmen: Nachrichten senden, empfangen, bestätigen, Synchronisation - \item Knotenausfälle behandeln + \item parallele Ausführung + \item getrennte Teilaufgaben (Micro-Services) \end{itemize*} - \color{orange} $\Rightarrow$ brauchen Modelle zur Beschreibung der Kommunikation \color{black} - - \subsubsection{Anforderungen} - \color{orange} Anforderungen an Kommunikationsmodelle in ... \color{black} - \newline - ... verteilten Systemen\newline \newline + \item Informationsaustausch \begin{itemize*} - \item Korrektheit - \item Sicherheit - \item Verfügbarkeit - \item Skalierbarkeit - \item Heterogenität + \item Email, Messenger + \item verteilte Algorithmen \end{itemize*} - - ...verteilten Verkehrsmanagementsystemen\newline \newline +\end{itemize*} + +\subsubsection{Software Architekturen} +\begin{enumerate*} + \item Früher: Hardware, Betriebssystem, Anwendung \begin{itemize*} - \item Echtzeitfähigkeit - \item Offenheit - \item Korrektheit, \newline Sicherheit - \item Skalierbarkeit, \newline Verfügbarkeit + \item Virtualisierung von Prozessor, Speicher, E/A Systemen + \item Interprozesskommunikation (IPC) \end{itemize*} - - \color{orange} Anforderungen \color{black} an den Betrieb eines (großen) verteilten Systems + \item Middlewaresysteme: Hardware, OS, Middleware, Anwendung \begin{itemize*} - \item (Last-)Skalierbarkeit (Scale-out): - \begin{itemize*} - \item viele kleine Server - statt eines großen - \item neue Server nach Bedarf hinzuzufügen - \end{itemize*} - \item Funktionssicherheit (Safety) / IT-Sicherheit (Security) - \item Fehlertoleranz / Verfügbarkeit - \begin{itemize*} - \item Ausfälle von einzelnen Knoten kompensieren - \item Redundante Verarbeitung - \end{itemize*} - \item Offenheit / Interoperabilität - \begin{itemize*} - \item neue Knoten und Systeme einfach integrieren - \end{itemize*} - \item Transparenz - \begin{itemize*} - \item verstecke die vielen Server vor den Nutzern - \end{itemize*} + \item verteilte Dienste + \item Programmierparadigmen: RPC, Client/Server,... + \item Java, CORBA, ... \end{itemize*} - - \subsection{Grundlagen verteilter Programmierung in Java und Erlang} - \subsubsection{Sockets} + \item Heute: Virtualisierung \begin{itemize*} - \item Verteilte Programmierung: Wir müssen einen entfernten Computer ansprechen - \item benötigen: Adresse $\rightarrow$ IP-Adresse - \item da mehrere Dienste auf demselben Computer laufen lauscht jeder Dienst auf einem Port (Nummer) - \begin{itemize*} - \item Wichtige Ports: 80 WWW, 20 (FTP), 25 (SMTP) - \end{itemize*} - \item \textbf{Socket} beschreibt einen Endpunkt, dh. Adresse und Port in einem TCP (oder UDP) Netzwerk - \item \textbf{Server-Socket} wartet auf Verbindungen - \item \textbf{Client} initiiert Verbindung, ebenfalls über einen (Client-Socket) + \item VM Hypervisor: verstecken Hardware vor dem Betriebssystem + \item Docker: eine Anwendung pro Container \end{itemize*} - - \subsubsection{Sockets in Java} - Socket in dem package ''java.net.Socket'' +\end{enumerate*} + +\subsubsection{Herausforderungen} +\begin{itemize*} + \item viele verschiedene Computer/Server \begin{itemize*} - \item einen \textbf{ServerSocket} auf Port 4242 erstellen - \begin{center} + \item verschiedene Betriebssysteme + \item unterschiedliche Leistungsfähigkeit + \end{itemize*} + \item Systemkomponenten müssen miteinander kommunizieren + \item verteilte Algorithmen: Nachrichten senden, empfangen, bestätigen, Synchronisation + \item Knotenausfälle behandeln +\end{itemize*} +\color{orange} $\Rightarrow$ brauchen Modelle zur Beschreibung der Kommunikation \color{black} + +\subsubsection{Anforderungen} +\color{orange} Anforderungen an Kommunikationsmodelle in ... \color{black} +\newline +... verteilten Systemen +\begin{itemize*} + \item Korrektheit + \item Sicherheit + \item Verfügbarkeit + \item Skalierbarkeit + \item Heterogenität +\end{itemize*} + +...verteilten Verkehrsmanagementsystemen +\begin{itemize*} + \item Echtzeitfähigkeit + \item Offenheit + \item Korrektheit, Sicherheit + \item Skalierbarkeit, Verfügbarkeit +\end{itemize*} + +\color{orange} Anforderungen \color{black} an den Betrieb eines (großen) verteilten Systems +\begin{itemize*} + \item (Last-)Skalierbarkeit (Scale-out): + \begin{itemize*} + \item viele kleine Server - statt eines großen + \item neue Server nach Bedarf hinzuzufügen + \end{itemize*} + \item Funktionssicherheit (Safety) / IT-Sicherheit (Security) + \item Fehlertoleranz / Verfügbarkeit + \begin{itemize*} + \item Ausfälle von einzelnen Knoten kompensieren + \item Redundante Verarbeitung + \end{itemize*} + \item Offenheit / Interoperabilität + \begin{itemize*} + \item neue Knoten und Systeme einfach integrieren + \end{itemize*} + \item Transparenz + \begin{itemize*} + \item verstecke die vielen Server vor den Nutzern + \end{itemize*} +\end{itemize*} + +\subsection{Grundlagen verteilter Programmierung in Java und Erlang} +\subsubsection{Sockets} +\begin{itemize*} + \item Verteilte Programmierung: Wir müssen einen entfernten Computer ansprechen + \item benötigen: Adresse $\rightarrow$ IP-Adresse + \item da mehrere Dienste auf demselben Computer laufen lauscht jeder Dienst auf einem Port (Nummer) + \begin{itemize*} + \item Wichtige Ports: 80 WWW, 20 (FTP), 25 (SMTP) + \end{itemize*} + \item \textbf{Socket} beschreibt einen Endpunkt, dh. Adresse und Port in einem TCP (oder UDP) Netzwerk + \item \textbf{Server-Socket} wartet auf Verbindungen + \item \textbf{Client} initiiert Verbindung, ebenfalls über einen (Client-Socket) +\end{itemize*} + +\subsubsection{Sockets in Java} +Socket in dem package ''java.net.Socket'' +\begin{itemize*} + \item einen \textbf{ServerSocket} auf Port 4242 erstellen + \begin{lstlisting}[language=java] ServerSocket serverSocket = new ServerSocket(4242); - \end{center} - \item Warte \color{orange}blockierend \color{black} auf Verbindungen - \begin{center} + \end{lstlisting} + \item Warte \color{orange}blockierend \color{black} auf Verbindungen + \begin{lstlisting}[language=java] Socket client = serverSocket.accept(); - \end{center} - \item Client Socket erstellen und zum Server verbinden - \begin{center} + \end{lstlisting} + \item Client Socket erstellen und zum Server verbinden + \begin{lstlisting}[language=java] Socket client = new Socket("localhost",4242); - \end{center} - \item Sockets in ähnlicher Form in C++ - \end{itemize*} - - \paragraph{Sockets in Java - Beispiel} - - Echo Server (Serverseite) - \begin{lstlisting}[language=C++] + \end{lstlisting} + \item Sockets in ähnlicher Form in C++ +\end{itemize*} + +\paragraph{Sockets in Java - Beispiel} + +Echo Server (Serverseite) +\begin{lstlisting}[language=C++] ServerSocket server = new ServerSocket(4242); while(true){ try(Socket client = server.accept(); ){ @@ -4924,9 +4855,9 @@ thread.start(); catch ( Exception e) { e.printStackTrace(); } } \end{lstlisting} - - Echo Server (Clientseite) - \begin{lstlisting}[language=C++] + +Echo Server (Clientseite) +\begin{lstlisting}[language=C++] try(Socket server = new Socket("localhost", 4242); ){ Scanner in = new Scanner(client.getInputStream() ); PrintWriter out = new PrintWriter(server.getOutputStream(), true); @@ -4934,757 +4865,711 @@ thread.start(); System.out.println(in.nextLine()); } catch ( Exception e) { e.printStackTrace(); } -\end{lstlisting} - - \subsection{Aktormodell in Erlang} + \end{lstlisting} + +\subsection{Aktormodell in Erlang} +\begin{itemize*} + \item formales Modell für Nebenläufigkeit und Verteilung + \item Basis für verschiedene Programmiersprachen/Frameworks: Erlang, Akka (Scala/Java) + \item Prinzipien: \begin{itemize*} - \item formales Modell für Nebenläufigkeit und Verteilung - \item Basis für verschiedene Programmiersprachen/Frameworks: Erlang, Akka (Scala/Java) - \item Prinzipien: + \item Aktor kapselt Zustand und Verhalten + \item Aktoren sind aktiv + \item Aktoren kommunizieren durch Nachrichtenaustausch \begin{itemize*} - \item Aktor kapselt Zustand und Verhalten - \item Aktoren sind aktiv - \item Aktoren kommunizieren durch Nachrichtenaustausch - \begin{itemize*} - \item Nichtblockierendes Senden - \item Blockierendes Empfangen - \end{itemize*} + \item Nichtblockierendes Senden + \item Blockierendes Empfangen \end{itemize*} \end{itemize*} - - \subsubsection{Übersicht} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-Aktormodell.png} - \end{center} - +\end{itemize*} + +\subsubsection{Übersicht} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-Aktormodell.png} +\end{center} + +\begin{itemize*} + \item Aktormodell in Erlang nativ umgesetzt \begin{itemize*} - \item Aktormodell in Erlang nativ umgesetzt - \begin{itemize*} - \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt - \item bisher aber nur auf einem Knoten - \end{itemize*} - \item Programmbestandteile im Aktormodell - \begin{itemize*} - \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ ... end. - \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spwan(fun ...). - \item Empfangen von Nachrichten $\Rightarrow$ receive ... end. - \item Senden $\Rightarrow$ Pid ! Request. - \end{itemize*} - \item kein globaler Zustand + \item Sende- und Empfangsoperationen schon für parallele Programmierung benutzt + \item bisher aber nur auf einem Knoten \end{itemize*} - - \subsubsection{Kommunikation zwischen Erlangknoten} - Erlangknoten starten (sname = short name) - \begin{lstlisting}[language=erlang] + \item Programmbestandteile im Aktormodell + \begin{itemize*} + \item Verhaltensdefinition $\Rightarrow$ f() $\rightarrow$ ... end. + \item Erzeugen neuer Aktoren $\Rightarrow$ Pid = spwan(fun ...). + \item Empfangen von Nachrichten $\Rightarrow$ receive ... end. + \item Senden $\Rightarrow$ Pid ! Request. + \end{itemize*} + \item kein globaler Zustand +\end{itemize*} + +\subsubsection{Kommunikation zwischen Erlangknoten} +Erlangknoten starten (sname = short name) +\begin{lstlisting}[language=erlang] #erl -sname node1 -setcookie 1234 Eshell V11.0 (abort with ^G) (node1@localhost)1> -\end{lstlisting} - - Weiteren Erlangknoten starten (selber oder anderer PC) - \begin{lstlisting}[language=erlang] + \end{lstlisting} + +Weiteren Erlangknoten starten (selber oder anderer PC) +\begin{lstlisting}[language=erlang] #erl -sname node2 -setcookie 1234 Eshell V11.0 (abort with ^G) (node2@localhost)1> -\end{lstlisting} - - Liste der verbundenen Knoten - \begin{lstlisting}[language=erlang] + \end{lstlisting} + +Liste der verbundenen Knoten +\begin{lstlisting}[language=erlang] (node@localhost)1> nodes(). [] -\end{lstlisting} - - \subsubsection{Cookie-System} - \begin{itemize*} - \item verteilte Erlangknoten benötigen zur Kommunikation gemeinsames \color{orange} Magic Cookie \color{black} (Passwort) - \item Mehrere Varianten - \begin{itemize*} - \item Datei {\raise.17ex\hbox{$\scriptstyle\mathtt{\sim}$}}/.erlang.cookie - \item Erlang Funktion \newline - \color{blue} erlang:\color{OliveGreen}set\_cookie(node(), \color{blue}Cookie)\color{black} - \item Option: erl - setcookie Cookie - \end{itemize*} - \end{itemize*} - - \subsubsection{Verbindungsaufbau zwischen Erlangknoten} - Verbindungsaufbau mittels \color{blue} net\_adm:\color{black}ping Funktion - - \begin{lstlisting}[language=erlang] -(node1@localhost)2> net_adm:ping("node2@localhost"). -pong -(node1@localhost)3> nodes(). -["node2@localhost"] + \end{lstlisting} -(node2@localhost)1> nodes(). -["node1@localhost"] -\end{lstlisting} - - \subsubsection{Kommunikation zwischen Erlangknoten} - Starten eines Prozesses auf einem entfernten Host - - \begin{lstlisting}[language=erlang] -complicated() -> - receive - {Sender, I} -> Sender ! I*I - end. +\subsubsection{Cookie-System} +\begin{itemize*} + \item verteilte Erlangknoten benötigen zur Kommunikation gemeinsames \color{orange} Magic Cookie \color{black} (Passwort) + \item Mehrere Varianten + \begin{itemize*} + \item Datei {\raise.17ex\hbox{$\scriptstyle\mathtt{\sim}$}}/.erlang.cookie + \item Erlang-Funktion + \begin{lstlisting}[language=erlang] + :set_cookie(node(), Cookie). + \end{lstlisting} + \item Option + \begin{lstlisting} + erl -setcookie Cookie + \end{lstlisting} + \end{itemize*} +\end{itemize*} -sender(Num, Pid) -> - Pid ! {self(), Num}, - receive - Res -> io:format("Result = ~p~n", [Res]) -(node1@localhost)4> N2Pid = spawn("node2@localhost", fun ch5_1:complicated/0) -(node1@localhost)5> ch5_1:sender(25, N2Pid). -Result = 625 -ok -\end{lstlisting} - - \subsection{Alternating Bit Protokoll} - \subsubsection{Übersicht} +\subsubsection{Verbindungsaufbau zwischen Erlangknoten} +Verbindungsaufbau mittels \color{blue} net\_adm:\color{black}ping Funktion + +\begin{lstlisting}[language=erlang] + (node1@localhost)2> net_adm:ping("node2@localhost"). + pong + (node1@localhost)3> nodes(). + ["node2@localhost"] + + (node2@localhost)1> nodes(). + ["node1@localhost"] + \end{lstlisting} + +\subsubsection{Kommunikation zwischen Erlangknoten} +Starten eines Prozesses auf einem entfernten Host + +\begin{lstlisting}[language=erlang] + complicated() -> + receive + {Sender, I} -> Sender ! I*I + end. + + sender(Num, Pid) -> + Pid ! {self(), Num}, + receive + Res -> io:format("Result = ~p~n", [Res]) + (node1@localhost)4> N2Pid = spawn("node2@localhost", fun ch5_1:complicated/0) + (node1@localhost)5> ch5_1:sender(25, N2Pid). + Result = 625 + ok + \end{lstlisting} + +\subsection{Alternating Bit Protokoll} +\subsubsection{Übersicht} +\begin{itemize*} + \item ermöglicht es, Nachrichten über einen verlustbehafteten Kommunikationskanal vollständig zu übertragen, sofern Verluste nur gelegentlich auftreten (transiente Fehler) + \item Empfänger quittiert jedes erhaltene Paket (Achnowledgement, kurz ACK) + \item \textbf{Achtung} Kanal kann Nachrichten und ACKs verlieren \begin{itemize*} - \item ermöglicht es, Nachrichten über einen verlustbehafteten Kommunikationskanal vollständig zu übertragen, sofern Verluste nur gelegentlich auftreten (transiente Fehler) - \item Empfänger quittiert jedes erhaltene Paket (Achnowledgement, kurz ACK) - \item \textbf{Achtung} Kanal kann Nachrichten und ACKs verlieren - \begin{itemize*} - \item benötigen je zwei unterschiedliche Sequenznummern und ACKs - \end{itemize*} - \item Empfänger liefert eine Nachricht nur beim ersten Empfang aus (keine Duplikate) - \item bei Timeout: Nachricht erneut senden - \item bei Erhalt eines unerwarteten ACKs: \textbf{aktuelle Nachricht erneut senden} - \end{itemize*} - - \subsubsection{Zustände} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-zustände-bit-protokoll.png} - \end{center} - - \subsubsection{Das Alternating Bit Protokoll} - Wir implementieren eine Variante, bei welcher: - \begin{itemize*} - \item der Sender zu Beginn eine Liste mit sämtlichen zu sendenden Nachrichten erhält, und - \item der Empfänger die erstmals empfangenen Nachrichten einfach auf dem Bildschirm ausgibt - \item alle Aktoren Statusmeldungen ausgeben - \item Verluste über einen Zufallszahlengenerator ausgelöst werden - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-alternate-bit-protokoll.png} - \end{center} - drei Prozesse mit \textbf{initialize(ErrorRate, NumberOfMessages, ReceiverPid, SenderPid, ChannelPid)} initialisieren und starten - \begin{itemize*} - \item Sender hat vier Zustandsfunktionen; Startet mit senderReady0(List): Liste mit Zahlen 1,...,NumberOfMessages - \item Kanal: Nachricht "verlieren", wenn Zufallszahl $\ngeq$ ErrorRate - \item Empfänger hat zwei Zustandsfunktionen; zu Beginn wird receiverWait0 gestartet - \item initialize wartet auf eine ready-Nachricht; sendet danach stop-Nachrichten an alle - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20015} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20016} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20017} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20018} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20019} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20020} - \end{center} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20021} - \end{center} - - \subsection{Kommunikationsmodelle \& Implementierungen} - \subsubsection{Kommunikationsmodelle} - Frage: Wie sollen Knoten miteinander kommunizieren? - \begin{itemize*} - \item Sprechen die Teilnehmer direkt miteinander oder über einen Vermittler? - \item Kann jeder jedem eine Nachricht schicken? - \item Wartet ein Teilnehmer darauf, dass seine Nachricht angekommen ist? - \item Wartet ein Teilnehmer darauf, dass eine Nachricht ankommt? - \item Muss ein Teilnehmer auf eine Nachricht antworten? + \item benötigen je zwei unterschiedliche Sequenznummern und ACKs \end{itemize*} + \item Empfänger liefert eine Nachricht nur beim ersten Empfang aus (keine Duplikate) + \item bei Timeout: Nachricht erneut senden + \item bei Erhalt eines unerwarteten ACKs: \textbf{aktuelle Nachricht erneut senden} +\end{itemize*} + +\subsubsection{Zustände} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-zustände-bit-protokoll} +\end{center} + +\subsubsection{Das Alternating Bit Protokoll} +Wir implementieren eine Variante, bei welcher: +\begin{itemize*} + \item der Sender zu Beginn eine Liste mit sämtlichen zu sendenden Nachrichten erhält, und + \item der Empfänger die erstmals empfangenen Nachrichten einfach auf dem Bildschirm ausgibt + \item alle Aktoren Statusmeldungen ausgeben + \item Verluste über einen Zufallszahlengenerator ausgelöst werden +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-alternate-bit-protokoll.png} +\end{center} +drei Prozesse mit \textbf{initialize(ErrorRate, NumberOfMessages, ReceiverPid, SenderPid, ChannelPid)} initialisieren und starten +\begin{itemize*} + \item Sender hat vier Zustandsfunktionen; Startet mit senderReady0(List): Liste mit Zahlen 1,...,NumberOfMessages + \item Kanal: Nachricht "verlieren", wenn Zufallszahl $\ngeq$ ErrorRate + \item Empfänger hat zwei Zustandsfunktionen; zu Beginn wird receiverWait0 gestartet + \item initialize wartet auf eine ready-Nachricht; sendet danach stop-Nachrichten an alle +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-57} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-58} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-59} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-60} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-61} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-62} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-63} +\end{center} + +\subsection{Kommunikationsmodelle \& Implementierungen} +\subsubsection{Kommunikationsmodelle} +Frage: Wie sollen Knoten miteinander kommunizieren? +\begin{itemize*} + \item Sprechen die Teilnehmer direkt miteinander oder über einen Vermittler? + \item Kann jeder jedem eine Nachricht schicken? + \item Wartet ein Teilnehmer darauf, dass seine Nachricht angekommen ist? + \item Wartet ein Teilnehmer darauf, dass eine Nachricht ankommt? + \item Muss ein Teilnehmer auf eine Nachricht antworten? +\end{itemize*} $\Rightarrow$ das Verhalten der Teilnehmer ist in \color{orange} Kommunikationsmodellen \color{black} beschrieben - - \subsubsection{Arten von Kommunikationsmodellen} - Es gibt viele verschiedene Modelle, z.B. für \color{orange} Botschaftenbasierte Modelle \color{black} + +\subsubsection{Arten von Kommunikationsmodellen} +Es gibt viele verschiedene Modelle, z.B. für \color{orange} Botschaftenbasierte Modelle \color{black} +\begin{itemize*} + \item Auftragsorientierte Modelle + \item Funktionsaufrufbasierte Modelle + \item Blackboards + \item Ereignisbasierte Modelle + \item Strombasierte Modelle + \item Wissensbasierte Modelle +\end{itemize*} +Kommunikationspartner sind für uns: +\begin{itemize*} + \item Threads/Prozesse innerhalb verteilter Anwendungen + \item Komponenten verteilter Systeme (Browser $\Leftrightarrow$ Webserver, DB Client $\Leftrightarrow$ DB-Server) +\end{itemize*} + +\subsubsection{Modellbestandteile} +\begin{itemize*} + \item \color{orange} Rollenmodell: \color{black} \begin{itemize*} - \item Auftragsorientierte Modelle - \item Funktionsaufrufbasierte Modelle - \item Blackboards - \item Ereignisbasierte Modelle - \item Strombasierte Modelle - \item Wissensbasierte Modelle + \item gemeinsames Handlungsmuster festlegen + \item z.B. Anrufer/Angerufener, Clinet/Server, Quelle/Senke \end{itemize*} - Kommunikationspartner sind für uns: + \item \color{orange} Datenmodell: \color{black} \begin{itemize*} - \item Threads/Prozesse innerhalb verteilter Anwendungen - \item Komponenten verteilter Systeme (Browser $\Leftrightarrow$ Webserver, DB Client $\Leftrightarrow$ DB-Server) + \item einheitliche Interpretation der ausgetauschten Daten + \item z.B. Dateiformate (XML/JSON), Kodierungen (MPEG4/H.264) \end{itemize*} - - \subsubsection{Modellbestandteile} + \item \color{orange} Fehlersemantiken \color{black} \begin{itemize*} - \item \color{orange} Rollenmodell: \color{black} - \begin{itemize*} - \item gemeinsames Handlungsmuster festlegen - \item z.B. Anrufer/Angerufener, Clinet/Server, Quelle/Senke - \end{itemize*} - \item \color{orange} Datenmodell: \color{black} - \begin{itemize*} - \item einheitliche Interpretation der ausgetauschten Daten - \item z.B. Dateiformate (XML/JSON), Kodierungen (MPEG4/H.264) - \end{itemize*} - \item \color{orange} Fehlersemantiken \color{black} - \begin{itemize*} - \item Einvernehmen über Wirkungen von Ausfällen - \item Eigenschaften von Kommunikationsoperationen müssen bei Ausfällen garantiert werden - \end{itemize*} - \item \color{orange} Terminierungssemantik \color{black} - \begin{itemize*} - \item Einvernehmen über das Ende der Kommunikation - \item Garantien über das Ende von Kommunikationsoperationen (auch bei Ausfällen) - \end{itemize*} + \item Einvernehmen über Wirkungen von Ausfällen + \item Eigenschaften von Kommunikationsoperationen müssen bei Ausfällen garantiert werden \end{itemize*} - - \subsubsection{Kommunikationsarten} + \item \color{orange} Terminierungssemantik \color{black} \begin{itemize*} - \item Wann ist eine Kommunikationsoperation abgeschlossen? - \item entspricht Terminierungssemantik - \item zwei grundlegende Arten: - \begin{itemize*} - \item \color{orange} synchron \color{black} - \begin{itemize*} - \item blockierend - \item Teilnehmer wartet bis die Gegenseite bereit ist - \item kann lange dauern, Sender kann nicht weiter arbeiten - \item Senden: Botschaftenankunft garantiert, einfache Implementierung synchroner Aktivitäten - \item Empfangen: Botschaftenankunft einfach und präzise feststellbar - \end{itemize*} - \item \color{orange} asynchron \color{black} - \begin{itemize*} - \item nicht-blockierend - \item Der Teilnehmer wartet nicht auf die Gegenseite (”fire and forget”) - \item unklar ob Botschaft angekommen - \item Senden: einfache Implementierung von Nebenläufigkeit - \item Empfangen: unklar wann Botschaft ankommt, einfache Implementierung von Nebenläufigkeit - \end{itemize*} - \item gilt sowohl für das Senden als auch das Empfangen - \end{itemize*} + \item Einvernehmen über das Ende der Kommunikation + \item Garantien über das Ende von Kommunikationsoperationen (auch bei Ausfällen) \end{itemize*} - - \paragraph{Kommunikationsarten: Senden} - - \color{orange} synchrones Senden: \color{black} Der Sender wartet bis der Empfänger die Botschaft annimmt - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20024} - \end{center} - \noindent \color{orange} asynchrones Senden: \color{black} Der Sender wartet nicht bis der Empfänger die Botschaft annimmt ("fire and forget" Prinzip) - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20025} - \end{center} - - \paragraph{Synchrones vs. asynchrones Senden} - +\end{itemize*} + +\subsubsection{Kommunikationsarten} +\begin{itemize*} + \item Wann ist eine Kommunikationsoperation abgeschlossen? + \item entspricht Terminierungssemantik + \item zwei grundlegende Arten: \begin{itemize*} - \item synchrones Senden + \item \color{orange} synchron \color{black} \begin{itemize*} - \item kann lange dauern, der Sender kann währenddessen nicht weiterarbeiten - \item die Botschaftenankunft ist garantiert, eine einfache Implementierung synchroner Aktivitäten + \item blockierend + \item Teilnehmer wartet bis die Gegenseite bereit ist + \item kann lange dauern, Sender kann nicht weiter arbeiten + \item Senden: Botschaftenankunft garantiert, einfache Implementierung synchroner Aktivitäten + \item Empfangen: Botschaftenankunft einfach und präzise feststellbar \end{itemize*} - \item asynchrones Senden + \item \color{orange} asynchron \color{black} \begin{itemize*} - \item unklar ob die Botschaft angekommen ist - \item einfache Implementierung von Nebenläufigkeit + \item nicht-blockierend + \item Der Teilnehmer wartet nicht auf die Gegenseite (”fire and forget”) + \item unklar ob Botschaft angekommen + \item Senden: einfache Implementierung von Nebenläufigkeit + \item Empfangen: unklar wann Botschaft ankommt, einfache Implementierung von Nebenläufigkeit \end{itemize*} + \item gilt sowohl für das Senden als auch das Empfangen \end{itemize*} - - \paragraph{Kommunikationsarten: Empfangen} - - \color{orange} synchrones Empfangen: \color{black} Der Empfänger wartet bis die Botschaft eintrifft - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20026} - \end{center} - \color{orange} asynchrones Empfangen: \color{black} Der Empfänger macht weiter, falls keine Nachricht eingetroffen ist - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20027} - \end{center} - - \paragraph{Synchrones vs. asynchrones Empfangen} - +\end{itemize*} + +\subsubsection{Kommunikationsarten: Senden} + +\color{orange} synchrones Senden: \color{black} Der Sender wartet bis der Empfänger die Botschaft annimmt +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-synchrones-senden} +\end{center} +\noindent \color{orange} asynchrones Senden: \color{black} Der Sender wartet nicht bis der Empfänger die Botschaft annimmt ("fire and forget" Prinzip) +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-asynchrones-senden} +\end{center} + +\paragraph{Synchrones vs. asynchrones Senden} + +\begin{itemize*} + \item synchrones Senden \begin{itemize*} - \item synchrones Empfangen: - \begin{itemize*} - \item kann lange dauern, der Sender kann nicht weiterarbeiten - \item Botschaftenankunft ist einfach und präzise feststellbar - \end{itemize*} - \item asynchrones Empfangen: - \begin{itemize*} - \item unklar wann die Botschaft ankommt; - \newline Benachrichtigungstechniken - \begin{itemize*} - \item Nachfragen (Polling) - \item ankommende Botschaft erzeugt neuen Thread beim Empfänger - \item weitere Techniken möglich - \end{itemize*} - \item einfache Implementierung von Nebenläufigkeit - \end{itemize*} + \item kann lange dauern, der Sender kann währenddessen nicht weiterarbeiten + \item die Botschaftenankunft ist garantiert, eine einfache Implementierung synchroner Aktivitäten \end{itemize*} - - \subsubsection{Fehlerbehandlung} + \item asynchrones Senden \begin{itemize*} - \item unverlässliches vs. verlässliches Senden - \begin{itemize*} - \item "Brief vs. Einschreiben" - \end{itemize*} - \item verlässliche Kommunikation erfordert - \begin{itemize*} - \item Quittierungen (Acknowledgements) $\rightarrow$ mehr Daten senden - \item Timeouts $\rightarrow$ Zeitverwaltung, langes Warten - \end{itemize*} - \item vielfältige Fehlermöglichkeiten in verteilten Anwendungen: - \begin{itemize*} - \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder verzögert zugestellt - \item Serverausfall: Nachricht empfangen? Operation ausgeführt? - \item Clientausfall: Aufruf gültig? Bestätigung erhalten? - \end{itemize*} + \item unklar ob die Botschaft angekommen ist + \item einfache Implementierung von Nebenläufigkeit \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20028} - \end{center} - - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20029} - \end{center} - - vielfältige Fehlermöglichkeiten in verteilten Anwendungen: +\end{itemize*} + +\subsubsection{Kommunikationsarten: Empfangen} + +\color{orange} synchrones Empfangen: \color{black} Der Empfänger wartet bis die Botschaft eintrifft +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-synchron-empfangen} +\end{center} +\color{orange} asynchrones Empfangen: \color{black} Der Empfänger macht weiter, falls keine Nachricht eingetroffen ist +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-asynchron-empfangen} +\end{center} + +\paragraph{Synchrones vs. asynchrones Empfangen} +\begin{itemize*} + \item synchrones Empfangen: \begin{itemize*} - \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder nur verzögert zugestellt + \item kann lange dauern, der Sender kann nicht weiterarbeiten + \item Botschaftenankunft ist einfach und präzise feststellbar + \end{itemize*} + \item asynchrones Empfangen: + \begin{itemize*} + \item unklar wann die Botschaft ankommt; + \newline Benachrichtigungstechniken + \begin{itemize*} + \item Nachfragen (Polling) + \item ankommende Botschaft erzeugt neuen Thread beim Empfänger + \item weitere Techniken möglich + \end{itemize*} + \item einfache Implementierung von Nebenläufigkeit + \end{itemize*} +\end{itemize*} + +\subsubsection{Fehlerbehandlung} +\begin{itemize*} + \item unverlässliches vs. verlässliches Senden + \begin{itemize*} + \item "Brief vs. Einschreiben" + \end{itemize*} + \item verlässliche Kommunikation erfordert + \begin{itemize*} + \item Quittierungen (Acknowledgements) $\rightarrow$ mehr Daten senden + \item Timeouts $\rightarrow$ Zeitverwaltung, langes Warten + \end{itemize*} + \item vielfältige Fehlermöglichkeiten in verteilten Anwendungen: + \begin{itemize*} + \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder verzögert zugestellt \item Serverausfall: Nachricht empfangen? Operation ausgeführt? \item Clientausfall: Aufruf gültig? Bestätigung erhalten? - \item Beispiel: Reisebuchung - \begin{itemize*} - \item Buchung durchgeführt? Bestätigung erhalten? - \item Bei wiederholter Ausführung: wirklich neue Buchung? - \end{itemize*} \end{itemize*} - - \paragraph{Fehlerbehandlung in Erlang} - - \begin{itemize*} - \item Timeout beim Warten auf Nachrichten - \item Wenn keine passende Nachricht innerhalb \textbf{Time} msecs empfangen wird, dann wird der Rückgabewert des \textbf{after}-Ausdrucks verwendet. - \end{itemize*} - \begin{lstlisting}[ - language=erlang, - ] -receive - {ok, Resp} $\rightarrow$ Resp; - {notfound} $\rightarrow$ notfound; - after Time $\rightarrow$ timeout -end -\end{lstlisting} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20030} - - Umgang mit Fehlern (Timeouts, Ausfälle): - \begin{itemize*} - \item Maybe: - \begin{itemize*} - \item keine Wiederholung - \item keine Ausführungsgarantie - \end{itemize*} - \item At-least-once: - \begin{itemize*} - \item wiederholte Ausführung, aber keine Erkennung von Nachrichtduplikaten - \item nur für idempotente Operationen (Lesen) - \end{itemize*} - \item At-most-once: - \begin{itemize*} - \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen - \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikateliminierung) - \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) - \end{itemize*} - \end{itemize*} - - \end{center} - \subsubsection{Überwachung von Erlang-Prozessen} - \begin{itemize*} - \item Linking von Prozessen: \color{green}link\color{blue}(Pid) \color{black} - \item M überwacht S; S bricht durch Fehler ab - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20031} - \end{center} - \item M wartet auf EXIT Nachricht von S $\rightarrow$ asynchroner Handler nötig - \end{itemize*} - - \subsubsection{on\_exit-Handler} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20032} - \end{center} - - \begin{itemize*} - \item überwacht den Prozess \textbf{Pid} auf Abbruch - \item Anwendungsspezifische Reaktionen möglich - \begin{itemize*} - \item Fehlermeldung - \item Neustart des Prozesses - \end{itemize*} - \item auch über Erlang-Knotengrenzen hinweg! - \end{itemize*} - - \paragraph{Anwendung des on\_exit-Handlers} - - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20033} - \end{center} - - \begin{itemize*} - \item Funktion anlegen (Liste in Atom konvertieren) - \item Prozess erzeugen - \item \textbf{on\_exit}-Handler definieren - \item Fehler verursachen (Nachricht ist keine Liste) - \end{itemize*} - - \subsubsection{Fehlersemantiken} - Umgang mit Fehlern (Timeouts, Ausfälle) - \begin{itemize*} - \item \color{orange} Maybe: \color{black} - \begin{itemize*} - \item keine Wiederholung - \item keine Ausführungsgarantie - \end{itemize*} - \item \color{orange} At-least-once: \color{black} - \begin{itemize*} - \item wiederholte Ausführung, aber keine Erkennung von Nachrichtenduplikaten - \item nur für idempotente Optionen (Lesen) - \end{itemize*} - \item \color{orange} At-most-once: \color{black} - \begin{itemize*} - \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen - \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikatelliminierung) - \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) - \end{itemize*} - \end{itemize*} - - \subsubsection{Auftragsorientierte Modelle} - \begin{itemize*} - \item klassische Modell serviceorientierten Systemdesigns - \item in verteilten Systemen: - \begin{itemize*} - \item Menge von Dienstanbietern (Server) - \item Menge von Clients, die diese Dienste nutzen wollen - \end{itemize*} - \end{itemize*} - Typische Anwendungsszenarien - \begin{itemize*} - \item \textbf{DB-Server}: verwalten Datenbestände, verarbeiten SQL Anfragen - \begin{itemize*} - \item Clients: "Gib mir alle Personen, die älter als 18 Jahre alt sind" - \end{itemize*} - \item \textbf{Web}: Webserver stellt HTML Dokumente bereit, Brwoser ruft URLs für Dokumente auf - \item \textbf{E-Mail}: Mailserver verwalten Postfächer, leiten Mails weiter, Outlook/Thunderbird/...senden/lesen von Emails - \item Namensdienste (DNS), Fileserver, Zeitserver (NTP) - \end{itemize*} - - \paragraph{Auftragsorientierte Modelle: Modellsicht} - - \begin{itemize*} - \item Rollenmodell: Clients erteilen Aufträge an Server - \item Datenmodell: Notschaften mit vereinbarter Struktur (Protokoll) - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20034} - \end{center} - \begin{itemize*} - \item Fehlersemantiken: Was ist der Grund, wenn ich keine Antwort erhalte? - \begin{itemize*} - \item Auftrag angekommen? Vollständig bearbeitet? - \item Was passiert wenn ein Auftrag wiederholt wird? - \end{itemize*} - \item Terminierungssemantiken: - \begin{itemize*} - \item Auftragserteilung in der Regel synchron - \item es existieren aber auch asynchrone Aufträge - \end{itemize*} - \end{itemize*} - - \paragraph{Auftragsorientierte Modelle: Implementierung} - - \begin{itemize*} - \item Implementierung aufbauend auf send/receive - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20035} - \end{center} \ \linebreak - - \paragraph{Ein Fileserver in Java} - - Server - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20036} - \end{center} - Erläuterungen zum Server - \begin{itemize*} - \item Zeile 1 \& 2: Serversocket erstellen, lauscht auf Port 4242, wartet blockierend bis sich ein Client verbindet - \item Zeile 6: lies eine Zeile vom Client - \item Zeile 7: unser Nachrichtenformat: \textbf{Operation $<$Leerzeichen$>$ Dateipfad} - \item Zeile 8ff: unterscheide Operationen und führe Aktionen aus; antworte dem Client entsprechend - \end{itemize*} - - Client - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20037} - \end{center} - \begin{itemize*} - \item Zeile 1: erstelle Clientsocket, d.h. Verbindungsaufbau zum Server auf localhost auf Port 4242 - \item Zeile 2: lese Befehl und Dateipfad - \item Zeile 4: sende Befehl als String an den Server - \item Zeile 6ff: lese alle Antwortzeilen vom Server; Ausgabe auf dem Bildschirm - \end{itemize*} - - \paragraph{Auftragsorientierte Modelle} - - \begin{itemize*} - \item Können benutzt werden, um einfache Protokolle zu implementieren - \item Binär oder ASCII - \begin{itemize*} - \item auch Übertragung komplexer Objekte möglich - \end{itemize*} - \item gesendeter Befehl könnte einer Methode/Funktion auf dem Server entsprechen - \begin{itemize*} - \item es erfolgt eine Art entfernter Funktionsaufruf - \item RPC wird im nächsten Abschnitt behandelt - \end{itemize*} - \item Funktionalität kann über das Internet angeboten werden - \begin{itemize*} - \item[$\Rightarrow$] Implementierung eines Webservices - \end{itemize*} - \end{itemize*} - - \subsubsection{Webservices - Allgemein} - \begin{itemize*} - \item WebService: Dienst, der über das Internet/WWW von Clients angesprochen werden kann - \item typischerweise über HTTP - \item Früher \textbf{SOAP}: Simple Object Access Protocol - \begin{itemize*} - \item Protokoll zum Austausch von Informationen in XML - \item Verzeichnisdienste zum Finden von Diensten, z.B. UDDI - \end{itemize*} - \item Heute \textbf{REST} - \end{itemize*} - - \subsubsection{REST} - \begin{itemize*} - \item Die Grundidee von REST: - \begin{itemize*} - \item \textbf{REST}: Representional State Transfer - \item oftmals existiert ein HTTP Server / Anwendungsserver schon - \item Idee: Jede Ressource die vom Server angeboten wird, ist durch eine URI beschrieben/identifiziert - \begin{itemize*} - \item Datei, ein Eintrag in einer Datenbank, Tweet,... - \end{itemize*} - \item Anlegen, Lesen, Verändern, Löschen (CRUD) - \begin{itemize*} - \item Art der Operation über HTTP Request-Typ festlegen (POST, GET, PUT, DELETE) - \end{itemize*} - \item Unabhängigkeit von verwendeter Programmiersprache in Client und Server durch HTTP und Textformate - \end{itemize*} - \end{itemize*} - - \paragraph{Anforderungen an Ressourcen} - - Anforderungen an Ressourcen nach Fielding: - \begin{enumerate*} - \item Adressierbarkeit: jede Ressource muss über URI adressierbar sein (Achtung: URI != URL, Identifier vs. Locator) - \item Zustandslosigkeit: Kommunikation zwischen Client und Server hat keinen Zustand (Session/Cookie) - \begin{itemize*} - \item bei jeder Anfrage werden alle Informationen gesendet - \end{itemize*} - \item Einheitliche Schnittstelle: über HTTP Standardmethoden auf Ressourcen zugreifen - \item Entkopplung von Ressource und Repräsentation: Ressourcen können in verschiedenen Formaten angeboten werden (JSON, XML,...) - \end{enumerate*} - - \paragraph{HTTP Methoden für REST} - - \begin{itemize*} - \item selbe URL mit verschiedenen Methoden aufrufbar - \item Methode bestimmt ausgeführte Aktion auf dem Server - \end{itemize*} - \begin{itemize*} - \item[GET:] eine Ressource lese, Daten sollten nicht verändert werden - \item[POST:] neue Ressource erstellen - \begin{itemize*} - \item Die URI ist dem Anrufer zunächst unbekannt - \item Der Server kann dem Anrufer die erzeugte URI in der Antwort mitteilen - \end{itemize*} - \item[PUT:] neue Ressource erstellen, oder existierende bearbeiten - \item[DELETE:] zum Löschen von Ressourcen - \end{itemize*} - - \paragraph{REST - Beispiel} - - Spotify API - \begin{itemize*} - \item \textbf{Authorization}-Header benötigt - \item \textbf{id}: Spotify-ID eines Künstlers - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20038} - \end{center} - - \paragraph{Implementierung von RESTful Webservices} - - \begin{itemize*} - \item manuelle Implementierung recht aufwändig - \begin{itemize*} - \item unterscheiden von HTTP Methoden (GET, POST,...) - \item parsen/prüfen von URL Pfaden und Parametern - \item setzen von Antwortheadern \& Kodierung in XML/JSON - \end{itemize*} - \item REST Frameworks erleichtern die Arbeit deutlich - \begin{itemize*} - \item JAX-RS Spezifikation für Java zur Erstellung von RESTful Services - \begin{itemize*} - \item Implementierung: Jersey: https://eclipse-ee4j.github.io/jersey/ - \item Implementierung: Spring: - https://spring.io/guides/gs/rest-service/ - \end{itemize*} - \item Microsofts \textbf{cpprestsdk} für C++ als Client-Bibliothek: - https://github.com/Microsoft/cpprestsdk - \end{itemize*} - \end{itemize*} - \begin{itemize*} - \item Beispiel: Jersey - \item Definition einer einfachen Klasse - \begin{itemize*} - \item Einstellungen über Annotationen - \item Klasse muss als Servlet in einem Applicationserver ausgeführt werden - \end{itemize*} - \end{itemize*} - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20039} - \end{center} - - \paragraph{Restful Webservice - Erläuterungen} - - \begin{itemize*} - \item Zeile 1: dieser Dienst ist über den Pfad files erreichbar, - z.B. http://localhost/files - \item Zeile 3: die nachfolgende Methode soll HTTP GET Anfragen - verarbeiten - \item Zeile 4: die URL enthält den Dateinamen als - Pfad-Bestandteil, z.B. - http://localhost/files/myfile.txt - \item Zeile 5: Hinweis an das Jersey-Framework das Ergebnis - automatisch ins JSON Format umzuwandeln - \item Zeile 6: normale Definition einer Methode \& Mapping des - Eingabeparameters auf den URL-Parameter - \item Zeile 8: das infos Objekt vom Typ FileInfo wird - automatisch als JSON repräsentiert - \end{itemize*} - - \paragraph{Aufruf von REST-Services} - - https://reques.in kostenloser Dienst zum Testen von REST-Clients - \begin{itemize*} - \item Variante 1: telnet reques.in 80 ... - \item Variante 2: Auf der Kommandozeile \newline - \$ curl https://reqres.in/api/users/1 - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20040} - \end{center} - \item Variante 3: Aufruf in einem Programm - \end{itemize*} - - \paragraph{HTTP GET Aufrufe in Java} - - In Java ab Version 11 eingebauter HTTP Client - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20041} - \end{center} - - \paragraph{HTTP POST in Java} - - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20042} - \end{center} - Antwort: - \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20043} - \end{center} - Eigentlich: JSON Ergebnis mit geeigneten Frameworks parsen und weiterverarbeiten - - \paragraph{Zusammenfassung} - - \begin{itemize*} - \item Auftragsorientierte Modelle nach dem Client-Server Prinzip - \item WebServices bieten Dienste über das WWW an - \item RESTful WebServices - \begin{itemize*} - \item jede Ressource hat eine URI - \item HTTP Methoden für Aktionen auf Ressourcen - \item unabhängig von Programmiersprachen - \end{itemize*} - \end{itemize*} - - \subsubsection{Funktionsaufrufbasierte Protokolle} - \begin{itemize*} - \item Grundidee: Adaption von anwendungsnahen und unkomplizierten Kommunikationsparadigmen an Eigenschaften verteilter Systeme - \item d.h., aus Aufrufen auf lokalen Prozeduren und Methoden werden Aufrufe entfernter Prozeduren und Methoden - \item bekannt als: - \begin{itemize*} - \item RPC: Remote Procedure Calls - \item oder Java RMI: Remote Method Invocation - \end{itemize*} - \item Erlang und Java haben die Konzepte nativ implementiert, in C++ nur über zusätzliche Bibliotheken - \end{itemize*} - - \paragraph{Eigenschaften von Prozedurfernaufrufen} - - Aufruf und Ausführung in unterschiedlichen Umgebungen/Kontexten - \begin{itemize*} - \item Programmiersprachen - \item Namens- und Adressräume - \item Betriebssystemkontext - \item Hardwarekontext - \end{itemize*} - Woher kennt der Aufrufer die Signatur der Prozedur auf dem Server? $\Rightarrow$ \color{orange} Stubs \color{black} +\end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20044} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-kommunikation-fehler} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-kommunikation-fehler-2} +\end{center} + +vielfältige Fehlermöglichkeiten in verteilten Anwendungen: +\begin{itemize*} + \item Kommunikations-/Netzwerkfehler: $\rightarrow$ Nachricht/Antwort gar nicht oder nur verzögert zugestellt + \item Serverausfall: Nachricht empfangen? Operation ausgeführt? + \item Clientausfall: Aufruf gültig? Bestätigung erhalten? + \item Beispiel: Reisebuchung + \begin{itemize*} + \item Buchung durchgeführt? Bestätigung erhalten? + \item Bei wiederholter Ausführung: wirklich neue Buchung? + \end{itemize*} +\end{itemize*} + +\paragraph{Fehlerbehandlung in Erlang} + +\begin{itemize*} + \item Timeout beim Warten auf Nachrichten + \item Wenn keine passende Nachricht innerhalb \textbf{Time} msecs empfangen wird, dann wird der Rückgabewert des \textbf{after}-Ausdrucks verwendet. +\end{itemize*} +\begin{lstlisting}[language=erlang] + receive + {ok, Resp} $\rightarrow$ Resp; + {notfound} $\rightarrow$ notfound; + after Time $\rightarrow$ timeout + end + \end{lstlisting} + +Umgang mit Fehlern (Timeouts, Ausfälle): +\begin{itemize*} + \item Maybe: + \begin{itemize*} + \item keine Wiederholung + \item keine Ausführungsgarantie + \end{itemize*} + \item At-least-once: + \begin{itemize*} + \item wiederholte Ausführung, aber keine Erkennung von Nachrichtduplikaten + \item nur für idempotente Operationen (Lesen) + \end{itemize*} + \item At-most-once: + \begin{itemize*} + \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen + \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikateliminierung) + \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) + \end{itemize*} +\end{itemize*} + +\subsubsection{Überwachung von Erlang-Prozessen} +\begin{itemize*} + \item Linking von Prozessen: \color{green}link\color{blue}(Pid) \color{black} + \item M überwacht S; S bricht durch Fehler ab + \begin{center} + \centering + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-erlang-überwachung} + \end{center} + \item M wartet auf EXIT Nachricht von S $\rightarrow$ asynchroner Handler nötig +\end{itemize*} + +\subsubsection{on\_exit-Handler} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-64} +\end{center} +\begin{itemize*} + \item überwacht den Prozess \textbf{Pid} auf Abbruch + \item Anwendungsspezifische Reaktionen möglich + \begin{itemize*} + \item Fehlermeldung + \item Neustart des Prozesses + \end{itemize*} + \item auch über Erlang-Knotengrenzen hinweg! +\end{itemize*} + +\paragraph{Anwendung des on\_exit-Handlers} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-65} +\end{center} + +\begin{itemize*} + \item Funktion anlegen (Liste in Atom konvertieren) + \item Prozess erzeugen + \item \textbf{on\_exit}-Handler definieren + \item Fehler verursachen (Nachricht ist keine Liste) +\end{itemize*} + +\subsubsection{Fehlersemantiken} +Umgang mit Fehlern (Timeouts, Ausfälle) +\begin{itemize*} + \item \color{orange} Maybe: \color{black} + \begin{itemize*} + \item keine Wiederholung + \item keine Ausführungsgarantie + \end{itemize*} + \item \color{orange} At-least-once: \color{black} + \begin{itemize*} + \item wiederholte Ausführung, aber keine Erkennung von Nachrichtenduplikaten + \item nur für idempotente Optionen (Lesen) + \end{itemize*} + \item \color{orange} At-most-once: \color{black} + \begin{itemize*} + \item garantiert, dass mehrfache Aufrufe nur zu einziger Ausführung führen + \item z.B. durch Sequenznummern (erfordert Protokollierung zur Duplikatelliminierung) + \item für nicht-idempotente Operationen (schreibend, z.B. Einfügen, Löschen) + \end{itemize*} +\end{itemize*} + +\subsubsection{Auftragsorientierte Modelle} +\begin{itemize*} + \item klassische Modell serviceorientierten Systemdesigns + \item in verteilten Systemen: + \begin{itemize*} + \item Menge von Dienstanbietern (Server) + \item Menge von Clients, die diese Dienste nutzen wollen + \end{itemize*} +\end{itemize*} +Typische Anwendungsszenarien +\begin{description*} + \item[DB-Server] verwalten Datenbestände, verarbeiten SQL Anfragen + \begin{itemize*} + \item Clients: "Gib mir alle Personen, die älter als 18 Jahre alt sind" + \end{itemize*} + \item[Web] Webserver stellt HTML Dokumente bereit, Browser ruft URLs für Dokumente auf + \item[E-Mail] Mailserver verwalten Postfächer, leiten Mails weiter, Outlook/Thunderbird/...senden/lesen von Emails + \item[Namensdienste (DNS), Fileserver, Zeitserver (NTP)] +\end{description*} + +\paragraph{Auftragsorientierte Modelle: Modellsicht} + +\begin{itemize*} + \item Rollenmodell: Clients erteilen Aufträge an Server + \item Datenmodell: Notschaften mit vereinbarter Struktur (Protokoll) +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-68} +\end{center} +\begin{itemize*} + \item Fehlersemantiken: Was ist der Grund, wenn ich keine Antwort erhalte? + \begin{itemize*} + \item Auftrag angekommen? Vollständig bearbeitet? + \item Was passiert wenn ein Auftrag wiederholt wird? + \end{itemize*} + \item Terminierungssemantiken: + \begin{itemize*} + \item Auftragserteilung in der Regel synchron + \item es existieren aber auch asynchrone Aufträge + \end{itemize*} +\end{itemize*} + +\paragraph{Auftragsorientierte Modelle: Implementierung} + +\begin{itemize*} + \item Implementierung aufbauend auf send/receive +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-kommunikation} +\end{center} \ \linebreak + +\paragraph{Ein Fileserver in Java} + +Server +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-69} +\end{center} +Erläuterungen zum Server +\begin{itemize*} + \item Zeile 1 \& 2: Serversocket erstellen, lauscht auf Port 4242, wartet blockierend bis sich ein Client verbindet + \item Zeile 6: lies eine Zeile vom Client + \item Zeile 7: unser Nachrichtenformat: \textbf{Operation $<$Leerzeichen$>$ Dateipfad} + \item Zeile 8ff: unterscheide Operationen und führe Aktionen aus; antworte dem Client entsprechend +\end{itemize*} + +Client +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-70} +\end{center} +\begin{itemize*} + \item Zeile 1: erstelle Clientsocket, d.h. Verbindungsaufbau zum Server auf localhost auf Port 4242 + \item Zeile 2: lese Befehl und Dateipfad + \item Zeile 4: sende Befehl als String an den Server + \item Zeile 6ff: lese alle Antwortzeilen vom Server; Ausgabe auf dem Bildschirm +\end{itemize*} + +\paragraph{Auftragsorientierte Modelle} + +\begin{itemize*} + \item Können benutzt werden, um einfache Protokolle zu implementieren + \item Binär oder ASCII + \begin{itemize*} + \item auch Übertragung komplexer Objekte möglich + \end{itemize*} + \item gesendeter Befehl könnte einer Methode/Funktion auf dem Server entsprechen + \begin{itemize*} + \item es erfolgt eine Art entfernter Funktionsaufruf + \item RPC wird im nächsten Abschnitt behandelt + \end{itemize*} + \item Funktionalität kann über das Internet angeboten werden + \begin{itemize*} + \item[$\Rightarrow$] Implementierung eines Webservices + \end{itemize*} +\end{itemize*} + +\subsubsection{Webservices - Allgemein} +\begin{itemize*} + \item WebService: Dienst, der über das Internet/WWW von Clients angesprochen werden kann + \item typischerweise über HTTP + \item Früher \textbf{SOAP}: Simple Object Access Protocol + \begin{itemize*} + \item Protokoll zum Austausch von Informationen in XML + \item Verzeichnisdienste zum Finden von Diensten, z.B. UDDI + \end{itemize*} + \item Heute \textbf{REST} +\end{itemize*} + +\subsubsection{REST} +\begin{itemize*} + \item Die Grundidee von REST: + \begin{itemize*} + \item \textbf{REST}: Representional State Transfer + \item oftmals existiert ein HTTP Server / Anwendungsserver schon + \item Idee: Jede Ressource die vom Server angeboten wird, ist durch eine URI beschrieben/identifiziert + \begin{itemize*} + \item Datei, ein Eintrag in einer Datenbank, Tweet,... + \end{itemize*} + \item Anlegen, Lesen, Verändern, Löschen (CRUD) + \begin{itemize*} + \item Art der Operation über HTTP Request-Typ festlegen (POST, GET, PUT, DELETE) + \end{itemize*} + \item Unabhängigkeit von verwendeter Programmiersprache in Client und Server durch HTTP und Textformate + \end{itemize*} +\end{itemize*} + +\paragraph{Anforderungen an Ressourcen} + +Anforderungen an Ressourcen nach Fielding: +\begin{enumerate*} + \item Adressierbarkeit: jede Ressource muss über URI adressierbar sein (Achtung: URI != URL, Identifier vs. Locator) + \item Zustandslosigkeit: Kommunikation zwischen Client und Server hat keinen Zustand (Session/Cookie) + \begin{itemize*} + \item bei jeder Anfrage werden alle Informationen gesendet + \end{itemize*} + \item Einheitliche Schnittstelle: über HTTP Standardmethoden auf Ressourcen zugreifen + \item Entkopplung von Ressource und Repräsentation: Ressourcen können in verschiedenen Formaten angeboten werden (JSON, XML,...) +\end{enumerate*} + +\paragraph{HTTP Methoden für REST} + +\begin{itemize*} + \item selbe URL mit verschiedenen Methoden aufrufbar + \item Methode bestimmt ausgeführte Aktion auf dem Server +\end{itemize*} +\begin{description*} + \item[GET] eine Ressource lese, Daten sollten nicht verändert werden + \item[POST] neue Ressource erstellen + \begin{itemize*} + \item Die URI ist dem Anrufer zunächst unbekannt + \item Der Server kann dem Anrufer die erzeugte URI in der Antwort mitteilen + \end{itemize*} + \item[PUT] neue Ressource erstellen, oder existierende bearbeiten + \item[DELETE] zum Löschen von Ressourcen +\end{description*} + +\paragraph{REST - Beispiel} + +Spotify API +\begin{itemize*} + \item \textbf{Authorization}-Header benötigt + \item \textbf{id}: Spotify-ID eines Künstlers +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-spotify-api} +\end{center} + +\paragraph{Implementierung von RESTful Webservices} + +\begin{itemize*} + \item manuelle Implementierung recht aufwändig + \begin{itemize*} + \item unterscheiden von HTTP Methoden (GET, POST,...) + \item parsen/prüfen von URL Pfaden und Parametern + \item setzen von Antwortheadern \& Kodierung in XML/JSON + \end{itemize*} + \item REST Frameworks erleichtern die Arbeit deutlich + \begin{itemize*} + \item JAX-RS Spezifikation für Java zur Erstellung von RESTful Services + \begin{itemize*} + \item Implementierung: Jersey: https://eclipse-ee4j.github.io/jersey/ + \item Implementierung: Spring: + https://spring.io/guides/gs/rest-service/ + \end{itemize*} + \item Microsofts \textbf{cpprestsdk} für C++ als Client-Bibliothek: + https://github.com/Microsoft/cpprestsdk + \end{itemize*} +\end{itemize*} +\begin{itemize*} + \item Beispiel: Jersey + \item Definition einer einfachen Klasse + \begin{itemize*} + \item Einstellungen über Annotationen + \item Klasse muss als Servlet in einem Applicationserver ausgeführt werden + \end{itemize*} +\end{itemize*} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-71} +\end{center} + +\paragraph{Restful Webservice - Erläuterungen} + +\begin{itemize*} + \item Zeile 1: dieser Dienst ist über den Pfad files erreichbar, + z.B. http://localhost/files + \item Zeile 3: die nachfolgende Methode soll HTTP GET Anfragen + verarbeiten + \item Zeile 4: die URL enthält den Dateinamen als + Pfad-Bestandteil, z.B. + http://localhost/files/myfile.txt + \item Zeile 5: Hinweis an das Jersey-Framework das Ergebnis + automatisch ins JSON Format umzuwandeln + \item Zeile 6: normale Definition einer Methode \& Mapping des + Eingabeparameters auf den URL-Parameter + \item Zeile 8: das infos Objekt vom Typ FileInfo wird + automatisch als JSON repräsentiert +\end{itemize*} + +\paragraph{Aufruf von REST-Services} + +https://reques.in kostenloser Dienst zum Testen von REST-Clients +\begin{itemize*} + \item Variante 1: telnet reques.in 80 ... + \item Variante 2: Auf der Kommandozeile \newline + \$ curl https://reqres.in/api/users/1 + \begin{center} + \centering + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-72} + \end{center} + \item Variante 3: Aufruf in einem Programm +\end{itemize*} + +\paragraph{HTTP GET Aufrufe in Java} + +In Java ab Version 11 eingebauter HTTP Client +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-73} +\end{center} + +\paragraph{HTTP POST in Java} + +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-74} +\end{center} +Antwort: +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-75} +\end{center} +Eigentlich: JSON Ergebnis mit geeigneten Frameworks parsen und weiterverarbeiten + +\paragraph{Zusammenfassung} + +\begin{itemize*} + \item Auftragsorientierte Modelle nach dem Client-Server Prinzip + \item WebServices bieten Dienste über das WWW an + \item RESTful WebServices + \begin{itemize*} + \item jede Ressource hat eine URI + \item HTTP Methoden für Aktionen auf Ressourcen + \item unabhängig von Programmiersprachen + \end{itemize*} +\end{itemize*} + +\subsubsection{Funktionsaufrufbasierte Protokolle} +\begin{itemize*} + \item Grundidee: Adaption von anwendungsnahen und unkomplizierten Kommunikationsparadigmen an Eigenschaften verteilter Systeme + \item d.h., aus Aufrufen auf lokalen Prozeduren und Methoden werden Aufrufe entfernter Prozeduren und Methoden + \item bekannt als: + \begin{itemize*} + \item RPC: Remote Procedure Calls + \item oder Java RMI: Remote Method Invocation + \end{itemize*} + \item Erlang und Java haben die Konzepte nativ implementiert, in C++ nur über zusätzliche Bibliotheken +\end{itemize*} + +\paragraph{Eigenschaften von Prozedurfernaufrufen} + +Aufruf und Ausführung in unterschiedlichen Umgebungen/Kontexten +\begin{itemize*} + \item Programmiersprachen + \item Namens- und Adressräume + \item Betriebssystemkontext + \item Hardwarekontext +\end{itemize*} +Woher kennt der Aufrufer die Signatur der Prozedur auf dem Server? $\Rightarrow$ \color{orange} Stubs \color{black} +\begin{center} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-netzwerk-stubs} \end{center} \subsubsection{Remote Procedure Calls (RPC)} @@ -5708,16 +5593,14 @@ Der Server-Stub/Skeleton \begin{itemize*} \item Vordefiniertes Erlang-Modul für RPC \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20045} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-76} \end{center} \item führt \textbf{Module:Func(Args)} auf \textbf{Node} aus \begin{itemize*} \item weitere Funktionen für asynchrone Aufrufe, Aufrufe von mehreren Servern \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20046} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-77} \end{center} \item andere Möglichkeit: eigene Funktionen über \textbf{register} anmelden (siehe Alternating Bit Protokoll) \item mit \textbf{whereis} PID von registrierten Erlang-Prozessen finden @@ -5740,8 +5623,7 @@ Der Server-Stub/Skeleton \paragraph{RMI - Schnittstelle für entfernte Objekte} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20047} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-78} \end{center} \paragraph{RMI: Server} @@ -5754,15 +5636,13 @@ Server-Objekt muss: \end{itemize*} Server-Objekt anlegen: \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20048} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-79} \end{center} \paragraph{RMI: Serverobjekt registrieren} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20049} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-80} \end{center} \paragraph{RMI - Client} @@ -5773,15 +5653,13 @@ Server-Objekt anlegen: \item Methode auf dem Server-Objekt aufrufen \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20050} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-81} \end{center} \paragraph{RMI - Ablauf} \begin{center} - \centering - \includegraphics[width=0.9\linewidth]{Assets/Programmierparadigmen-20051} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-82} \end{center} \paragraph{Interoperabilität von RPC} @@ -5795,7 +5673,7 @@ Lösungsansätze: \item \textbf{XML-RPC, JSON-RPC}: kodiere alle zum Aufruf nötigen Informationen als XML bzw. JSON \begin{itemize*} \item HTTP zur Übertragung - \item \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20052} + \item \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-83} \item \textbf{id} für die Zuordnung von Antworten zu Anfragen \end{itemize*} \item \textbf{gRPC}: Code-Generierung für Server, Stubs und ausgetauschte Daten @@ -5813,16 +5691,14 @@ Lösungsansätze: \item C/C++, Java, Python, GO, uvm. \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.45\linewidth]{Assets/Programmierparadigmen-20053} + \includegraphics[width=0.45\linewidth]{Assets/Programmierparadigmen-grpc} \end{center} \paragraph{gRPC: Dienstbeschreibung} fileservice.proto \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20054} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-84} \end{center} \paragraph{gRPC: Dienstbeschreibung - Erläuterungen} @@ -5841,8 +5717,7 @@ fileservice.proto \begin{itemize*} \item *.proto Dateien werden mittels protoc Compiler in die Zielsprache übersetzt \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20055} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-85} \end{center} \item erzuegt C++ Dateien für messages sowie Service-Klassen (FileService) \item Klasse \textbf{FileService} enthält generierten Stub und Methoden für den Server zum überschreiben @@ -5851,22 +5726,19 @@ fileservice.proto \paragraph{gRPC: Server erzeugen} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20056} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-86} \end{center} \paragraph{gRPC: Server starten} \begin{center} - \centering - \includegraphics[width=0.65\linewidth]{Assets/Programmierparadigmen-20057} + \includegraphics[width=0.65\linewidth]{Assets/Programmierparadigmen-code-snippet-87} \end{center} \paragraph{gRPC: Client} \begin{center} - \centering - \includegraphics[width=0.65\linewidth]{Assets/Programmierparadigmen-20058} + \includegraphics[width=0.65\linewidth]{Assets/Programmierparadigmen-code-snippet-88} \end{center} \subsubsection{Zusammenfassung} @@ -5882,7 +5754,7 @@ fileservice.proto \subsection{Weitere Kommunikationsmodelle und Cloud-Computing} \subsubsection{Blackboards} \begin{itemize*} - \item das "schwarze Brett": Teilnehmer hinterlegen + \item das 'schwarze Brett': Teilnehmer hinterlegen \begin{itemize*} \item Gesuche und Angebote \item Aufträge und Ergebnisse @@ -5891,8 +5763,7 @@ fileservice.proto \item implementiert zum Beispiel in JavaSpaces \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20059} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-javaSpaces} \end{center} \paragraph{Blackboards: Modell-Sicht} @@ -5956,8 +5827,7 @@ fileservice.proto \end{itemize*} \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20060} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-ereignisbasiertes-modell} \end{center} \paragraph{Ereignisbasierte Modelle: Modell-Sicht} @@ -6015,15 +5885,13 @@ RabbitMQ \end{itemize*} \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20061} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-rabbitmq} \end{center} \paragraph{RabbitMQ Publish} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20062} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-89} \end{center} \begin{itemize*} \item Zeilen 7,8: \textbf{ConnectionFactory} zum Handling von Verbindungen, im Beispiel nur auf dem lokalen Host @@ -6038,8 +5906,7 @@ RabbitMQ \paragraph{RabbitMQ Subscribe} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20063} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-90} \end{center} \begin{itemize*} \item Zeilen 1\&2: Connection und Channel erstellen, siehe vorherige Bilder @@ -6103,14 +5970,12 @@ Cloud Computing als Geschäftsmodell: \subsubsection{Architekturen} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20064} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-cloud-architekturen} \end{center} \subsubsection{Amazon AWS Produkte} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20065} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-aws-produkte} \end{center} Hinweis: Bei AWS, Google, Azure viele Dienste auch (dauerhaft) kostenlos nutzbar! @@ -6127,14 +5992,14 @@ Hinweis: Bei AWS, Google, Azure viele Dienste auch (dauerhaft) kostenlos nutzbar \item unterstützt durch Virtualisierung und Cloud-Angebote, z.B. Serverless Computing \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20066} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-microservice} \end{center} + \subsubsection{Serverless Computing} \begin{itemize*} - \item wörtlich: "ohne Server" + \item wörtlich: 'ohne Server' \begin{itemize*} - \item aber in Wirklichkeit: "es ist nicht dein Server" + \item aber in Wirklichkeit: 'es ist nicht dein Server' \item Idee: Entwickler konzentriert sich auf (kleine Funktion) \begin{itemize*} \item Function-as-a-Service (FaaS) @@ -6155,8 +6020,7 @@ Aufgaben des Entwicklers \end{itemize*} \end{itemize*} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20067} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-aws-lambda-designer} \end{center} AWS Lambda Designer: HTTP Trigger, auszuführende Funktion und Nachfolgefunktion. @@ -6181,13 +6045,12 @@ Mittlerweile auch Serverless Microservice: Microservice nicht immer ausführen, \subsubsection{AWS Lambda: Java Beispiel} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20068} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-91} \end{center} -\begin{itemize*} - \item \textbf{RequestHandler} als Einstiegspunkt für Lambda-Funktionen - \item \textbf{handleRequest} wird von der Lambda-Umgebung aufgerufen -\end{itemize*} +\begin{description*} +\item[RequestHandler] als Einstiegspunkt für Lambda-Funktionen +\item[handleRequest] wird von der Lambda-Umgebung aufgerufen +\end{description*} \subsubsection{Spring Functions} \begin{itemize*} @@ -6203,16 +6066,14 @@ Mittlerweile auch Serverless Microservice: Microservice nicht immer ausführen, \paragraph{Spring Cloud Function: Projekt anlegen} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20069} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-springio} \end{center} Projekt generieren, herunterladen und entpacken \paragraph{Spring Cloud Functions: Beispiel} \begin{center} - \centering - \includegraphics[width=0.7\linewidth]{Assets/Programmierparadigmen-20070} + \includegraphics[width=0.4\linewidth]{Assets/Programmierparadigmen-code-snippet-92} \end{center} \begin{itemize*} \item Zeile 1: für Spring Boot, diese Klasse enthält Definitionen für Dienste @@ -6232,13 +6093,22 @@ Projekt generieren, herunterladen und entpacken \paragraph{Aufruf der Spring Cloud Funktion} \begin{enumerate*} - \item Projekt kompilieren \newline ./mvnw clean install - \item Starten \newline + \item Projekt kompilieren + \begin{lstlisting} + ./mvnw clean install + \end{lstlisting} + \item Starten + \begin{lstlisting} java - jar target/fileinfo-0.0.1-SNAPSHOT.jar + \end{lstlisting} \item Aufruf als normaler REST call + \begin{lstlisting} \newline curl localhost:8080/info -d myfile.txt - \newline Antwort + \end{lstlisting} + \item Antwort + \begin{lstlisting} \newline \{"name":"myfile.txt","size":1234\} + \end{lstlisting} \end{enumerate*} \subsubsection{Zusammenfassung}