Kuba Werłos


O trzech złożonościach


Proste

vs

skomplikowane


                    <?php
                    function getTheAnswer()
                    {
                        return 42;
                    }
                

                    <?php
                    foreach ($valuesToPercentagesArray as $columnName => $sortOrder) {
                        foreach ($data as &$record) {
                            $values = $record[$columnName];
                            if ($values) {
                                $lookup = [];
                                foreach(explode(',', $values) as $v) {
                                    if (!empty($v)) {
                                        if (!isset($lookup[$v])) {
                                            $lookup[$v] = 1;
                                        } else {
                                            $lookup[$v]++;
                                        }
                                    }
                                }
                            }
                        }
                    }
                

Jak to zmierzyć?



  • statyczna analiza kodu
  • dług techniczny
  • testy

ZŁOŻONOŚĆ

Złożoność cyklomatyczna

(cyclomatic complexity)


                    <?php
                    function foo(int $x, int $y): void {
                        if ($x > 10) {
                            echo 1;
                        } else {
                            echo 2;
                        }

                        if ($x > $y) {
                            echo 3;
                        } else {
                            echo 4;
                        }
                    }
                

Złożoność cyklomatyczna


Thomas J. McCabe, Sr.

1976

  • 1—10: kod dość prosty, nieznaczne ryzyko
  • 11—20: kod złożony, ryzyko na średnim poziomie
  • 21—50: kod bardzo złożony, wysokie ryzyko
  • powyżej 50: kod niestabilny, bardzo wysoki poziom ryzyka

Złożoność cyklomatyczna


Narzędzia


PHP Mess Detector
phpmd/phpmd


                    <rule ref='rulesets/codesize.xml/CyclomaticComplexity'>
                        <properties>
                            <property name='reportLevel' value='10' />
                        </properties>
                    </rule>
                

Złożoność cyklomatyczna


WP_Query::get_posts

Jaka jest złożoność tej metody?


304 (6.0.2)

321 (master)

Złożoność poznawcza

(cognitive complexity)

Podstawowe zasady:

  • Ignorujemy struktury, które pozwalają na czytelne skrócenie wielu instrukcji w jedno
  • Zwiększamy (dodajemy 1) za każdą przerwę w liniowym przepływie kodu
  • Zwiększamy gdy struktury przerywające przepływ są zagnieżdżone

Złożoność poznawcza

(cognitive complexity)


                    <?php
                    function sumOfPrimes($max) {
                        $sum = 0;
                        for ($i = 1; $i < $max; ++$i) {    // +1
                            for ($j = 2; $j < $i; ++$j) {  // +2
                                if ($i % $j === 0) {       // +3
                                    continue 2;            // +1
                                }
                            }

                            $sum += $i;
                        }

                        return $sum;
                    }
                

Złożoność poznawcza

Cognitive complexity

Narzędzia

  • SonarQube: Cognitive Complexity of functions should not be too high
  • rarst/phpcs-cognitive-complexity
  • symplify/phpstan-rules — CognitiveComplexity

                    includes:
                      - vendor/(...)/cognitive-complexity-services.neon
                    services:
                      - class: Symplify\(...)\FunctionLikeCognitiveComplexityRule
                        tags: [phpstan.rules.rule]
                        arguments:
                          maxMethodCognitiveComplexity: 10
                

Złożoność poznawcza


WP_Query::get_posts

Jaka jest złożoność tej metody?


567 (6.0.2)

599 (master)

Złożoność obliczeniowa

(computational complexity)

czasowa lub pamięciowa


Notacja dużego Ο

(asymptotyczne tempo wzrostu)

Złożoność obliczeniowa


Ο(1)



                    <?php

                    function nthElement(array $array, int $index): mixed
                    {
                        return $array[$index];
                    }
                

Złożoność obliczeniowa


Ο(n)



                    <?php

                    function sumElements(array $array): float|int
                    {
                        return \array_sum($array);
                    }
                

Złożoność obliczeniowa


Ο(n^2)


                    <?php

                    function hasDuplicates(array $array): bool
                    {
                        foreach ($array as $x) {
                            foreach ($array as $y) {
                                if ($x === $y) {
                                    return true;
                                }
                            }
                        }

                        return false;
                    }
                

Złożoność obliczeniowa


Ο(log n)


                    <?php
                    function findElement(array $array, mixed $value): bool
                    {
                        return findInRange($array, $value, 0, \count($array) - 1);
                    }
                    function findInRange(array $array, mixed $v, int $from, int $to)
                    {
                        if ($from === $to) {
                            return $v === $array[$from];
                        }
                        $half = (int) (($to - $from) / 2);
                        return $v < $array[$half]
                            ? findInRange($array, $v, $from, $half)
                            : findInRange($array, $v, $half, $to);
                    }

                

Złożoność obliczeniowa


Ο(n log n)



                    <?php

                    function quicksort(array $array): float|int
                    {
                        return \sort($array);
                    }
                

Złożoność obliczeniowa


Ο(n^c)

c > 1


Czas wielomianowy

Np. naiwne rozwiązanie równania z c zmiennymi z rozwiązaniami mniejszymi od n.

5x + 42y + 7z = 106

Złożoność obliczeniowa


Ο(2^n)



                    <?php

                    function fibonacci(int $n): int
                    {
                        if ($n > 2) {
                            return fibonacci($n - 1) + fibonacci($n - 2);
                        }

                        return 1;
                    }
                

Złożoność obliczeniowa


Ο(n!)



                    <?php

                    function getPermutations(string $s): array
                    {
                        // ...
                    }
                

Złożoność obliczeniowa


Narzędzia


Problem stopu

Alan Turing udowodnił w 1936 roku,
że nie może istnieć ogólny algorytm
do rozstrzygnięcia problemu zatrzymania
dla wszystkich możliwych danych wejściowych.

Pytania?

Dziękuję za uwagę


werlos@gmail.com


GitHubkubawerlosGitter

https://kubawerlos.github.io/slides