Jakie są najlepsze sposoby do obsługi nulli?

0

Jakie sa najlepsze sposoby do sprawdzania nulli? Chodzi mi o zasady clean code (null check) etc etc w springu/javie. Warunki? Adnotacje? Moze macie jakis sensowny artykul, poczytałbym sobie. Z góry przepraszam za chaos, nie wiem do konca jak to przekazac ale mam nadzieje, ze wiecie o co chodzi:))

4

Option z Vavr

2

Najlepiej w ogóle nie używać nulli i sprawdzać to jakimś linterem (nie używanie).
Czasem można w kod wrzucać asercje (wywalamy się na null).
W przypadku bibliotek zewnętrznych opakować nulle w Option (z Vavr). Ewentualnie w troche kulawy Optional z javy. (nie zawsze - trzeba patrzeć na ile to
sensowne, ale ogólnie najlepiej jak nulle w ogóle nie latają w "domenie").

Najlepiej to po prostu nie używać Javy i przesiąśc się na np. Kotlina - gdzie problem jest rozwiązany dość pragmatycznie. (Szybko się np. przepakowuje jawowe niewiadomoco (czyli T!) na T albo T? albo Option<T>.

Jeśli chodzi o różnego rodzaju adnotacje (@Nullable, @NonNull) w Javie i jakiś FindBugs - to nigdy mi to w większym projekcie nie działało nawet średnio - tony fałszywych alarmów. Chyba najlepiej pomaga jako warningi w IDE (intellij) - ale rozwiązywanie tego typu problemów warningami w IDE to żadnie rozwiązanie - też nie działa.

0

@stanley123: chyba pomieszałeś dwie rzeczy.

Pierwsza to klasyczne null check z CC, czyli niezwracanie wartości null. Tutaj masz dwie opcje. Pierwsza wymieniona przez @KamilAdam, czyli dodatkowa biblioteka i klasa Option. Druga to Javovy Optional, który jest bardziej „nieporęczny” w użyciu.

Druga to walidacja wartości zwracanej. W takim wypadku w Springu można to ogarnąć za pomocą kombinacji Bean Validation z adnotacją @NotNull oraz programowania aspektowego, żeby ogarnąć wyjście z metody.

0

Dzieki chlopaki, zaglebie sie w option z vavr bo szczerze nigdy tego nie uzywalem..

0
stanley123 napisał(a):

Jakie sa najlepsze sposoby do sprawdzania nulli? Chodzi mi o zasady clean code (null check) etc etc w springu/javie.

Już samo pozwolenie aby null był możliwy, a szczególnie zwracany, jest dalekie od istoty myślenia Clean Code. Niestety w czasie powstawania javy (1995) nasza (czyli branży) refleksja nad problemami była o wiele, wiele mnie rozwinięta. Programowanie obiektowe dopiero się krystalizowało, na projektach tysiąckroć prostszych niż dziś itd...
Java cierpi na lata powstawania, nie mogła być w tamtym czasie radykalnie lepsza. Jej "młodszy brat" Kotlin wiele rzeczy ma przemyślanych lepiej

stanley123 napisał(a):

Warunki? Adnotacje? Moze macie jakis sensowny artykul, poczytałbym sobie. Z góry przepraszam za chaos, nie wiem do konca jak to przekazac ale mam nadzieje, ze wiecie o co chodzi:))

Większość uczestników tego wątku jest wielkimi krytykami adnotacji. Leczenie null adnotacjami to leczenie syfu cholerą

YT jest pełny materiałów z konferencji (w tym @jarekr000000 ) nt Javy i nulli

1
stanley123 napisał(a):

Jakie sa najlepsze sposoby do sprawdzania nulli? Chodzi mi o zasady clean code (null check) etc etc w springu/javie. Warunki? Adnotacje? Moze macie jakis sensowny artykul, poczytałbym sobie. Z góry przepraszam za chaos, nie wiem do konca jak to przekazac ale mam nadzieje, ze wiecie o co chodzi:))

A po co sprawdzać? Najlepiej w ogóle nie używać null, to jest zaszłość w języku moim zdaniem. Oczywiście, jak korzystasz z biblioteki która się takimi posługuje to powinieneś je jakoś ogarnąć, np owrapować w obiekt który umie je obsługiwać, ale jako zasada to najlepiej ich unikać w ogóle.

1
Riddle napisał(a):

A po co sprawdzać? Najlepiej w ogóle nie używać null, to jest zaszłość w języku moim zdaniem. Oczywiście, jak korzystasz z biblioteki która się takimi posługuje to powinieneś je jakoś ogarnąć, np owrapować w obiekt który umie je obsługiwać, ale jako zasada to najlepiej ich unikać w ogóle.

W idealnym przypadku, gdzie 100% kodu ma sie własne, ale nie w realnym życiu
W szerokim ekosytemie Javy null nie do unikniecia

2
ZrobieDobrze napisał(a):
Riddle napisał(a):

A po co sprawdzać? Najlepiej w ogóle nie używać null, to jest zaszłość w języku moim zdaniem. Oczywiście, jak korzystasz z biblioteki która się takimi posługuje to powinieneś je jakoś ogarnąć, np owrapować w obiekt który umie je obsługiwać, ale jako zasada to najlepiej ich unikać w ogóle.

W idealnym przypadku, gdzie 100% kodu ma sie własne, ale nie w realnym życiu
W szerokim ekosytemie Javy null nie do unikniecia

Weź nie gadaj bzdur.

Null Ci może wejść tylko z zależności jakiejś, najpewniej biblioteki, albo z biblioteki standardowej. Obie te rzeczy powinny być naookoło Twojej logiki, i powinny być wyabstraktowane.

0
Riddle napisał(a):

Weź nie gadaj bzdur.

Bzdurna jest rada "nie używaj nulla"

1
ZrobieDobrze napisał(a):
Riddle napisał(a):

Weź nie gadaj bzdur.

Bzdurna jest rada "nie używaj nulla"

To nie jest bzdurna rada, null to jest zaszłość z C, gdzie "null pointer" to faktycznie był null pointer, bo wskazywał na adres 0. W Javie nie ma czegoś takiego jak wskaźnik na randomowy adres w pamięci, są tylko referencje na obiekty i to tylko javowe obiekty. null to jest moim zdaniem nieudana próba odwzorowania tego zachowania z wskaźnikami.

Owszem, biblioteki czasem zwracają null, i owszem, biblioteka standardowa javy, zwłaszcza ta przed 7mką też je zwracała; ale nie znaczy wcale to że to jest dobre. Najlepsze miejsce to w swoim kodzie nie używać null, a jak się używa bibliotek lub funkcji które je zwracają, to należy je wyabstraktować, jak mówiłem, tak żeby do naszego kodu taki null nie trafił. Jak mówisz "przed nullami nie da się uciec", to brzmi trochę jakbyś się bał kodu z którym pracujesz i nie miał go pod kontrolą. Możesz użyć Optional albo wzorca EmptyObject, są też inne sposoby.

1
ZrobieDobrze napisał(a):

Większość uczestników tego wątku jest wielkimi krytykami adnotacji. Leczenie null adnotacjami to leczenie syfu cholerą

YT jest pełny materiałów z konferencji (w tym @jarekr000000 ) nt Javy i nulli

Akurat adnotacje "compile time" / "build time" doceniam i uważam, że mają sens. Tyle, że akurat te odnośnie null słabo po prostu działają w praniu (chyba głownie dlatego, że powstało tak z 7 różnych bibliotek z tymi adnotacjami i analizatoru kodu głupieją przy sprawdzaniu tych adnotacji).

Jeszcze mała uwaga - przed nullem da się uciec dość daleko, po prostu jak to w życiu nie zawsze i wszędzie warto.
Raczej nie warto wprowadzać nowych nulli (poza ekstrmalnie dziwnymi przypadkami - np. walczymy o cykle).
Natomiast nie zawsze faktycznie przykrywam (przykrywałem) te wszystkie nulle z biblioteki standardowej i frameworków - czasem po prostu szkoda czasu.

3
Riddle napisał(a):
ZrobieDobrze napisał(a):
Riddle napisał(a):

Weź nie gadaj bzdur.

Bzdurna jest rada "nie używaj nulla"

To nie jest bzdurna rada

To jest bzdurna rada ;)

Moim zdaniem wynika z dogmatyzmu i braku pragmatyzmu (typowe u ewangelistów różnych paradygmatów; z wiekiem przechodzi).

null jest bardziej memory efficient niż stosowanie obiektów, które go opakują w sposób "user friendly". W zasadzie chyba tyle z dobrych rzeczy. Nie oznacza, że należy go zawsze stosować i zawsze optymalizować użycie pamięci. Ot czasem się trafi jakiś niszowy projekt. Dodatkowo, czasem ludziom zdarza się pracować w niereformowalnych zespołach i nie pełni się roli ostatecznego decydenta, wówczas trzeba żyć z nullami (albo żyć dogmatami i zmienić zespół ;) )

Teraz będę trochę złośliwy. Jak to mówią, szewc bez butów chodzi, zobacz ile masz nulli w T-Regex, np. interfejs definiujesz, w którym zwracasz null. Gdzie te dobre praktyki?

<?php
namespace TRegx\CleanRegex\Match;

interface Structure
{
    public function subject(): string;

    /**
     * @return (string|null)[]
     */
    public function groupNames(): array;

    public function groupsCount(): int;

    /**
     * @param string|int $nameOrIndex
     * @return bool
     */
    public function groupExists($nameOrIndex): bool;
}

Gdzie te abstrakcje na nulle? Domyślne parametry?

public static function match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int
    {
        $pr = Bug::fix($pattern);
        return Guard::invoke('preg_match', $pr, static function () use ($pr, $subject, &$matches, $flags, $offset) {
            return @\preg_match($pr, $subject, $matches, $flags, self::offset($subject, $offset)) ? 1 : 0;
        });
    }

p.s.

To pytanie retoryczne, zapewne nulle są w php uzasadnione.

1
yarel napisał(a):
Riddle napisał(a):
ZrobieDobrze napisał(a):
Riddle napisał(a):

Weź nie gadaj bzdur.

Bzdurna jest rada "nie używaj nulla"

To nie jest bzdurna rada

To jest bzdurna rada ;)

Moim zdaniem wynika z dogmatyzmu i braku pragmatyzmu (typowe u ewangelistów różnych paradygmatów; z wiekiem przechodzi).

null jest bardziej memory efficient niż stosowanie obiektów, które go opakują w sposób "user friendly". W zasadzie chyba tyle z dobrych rzeczy.

Mikrooptymalizacja. Nie jesteś w stanie napisać kodu z nullami i potem przerobić na obiekty, tak żebyś w jakikolwiek sposób był w stanie to wykazać. Odpal sobie profiler Javy i spróbować oszczędzić chociaż milisemundę w ten sposób. Good luck.

Nie oznacza, że należy go zawsze stosować i zawsze optymalizować użycie pamięci. Ot czasem się trafi jakiś niszowy projekt. Dodatkowo, czasem ludziom zdarza się pracować w niereformowalnych zespołach i nie pełni się roli ostatecznego decydenta, wówczas trzeba żyć z nullami (albo żyć dogmatami i zmienić zespół ;) )

Ten sam argument możesz wyciągnąć do obrony każdej złej praktyki.

Teraz będę trochę złośliwy. Jak to mówią, szewc bez butów chodzi, zobacz ile masz nulli w T-Regex, np. interfejs definiujesz, w którym zwracasz null. Gdzie te dobre praktyki?

<?php
namespace TRegx\CleanRegex\Match;

interface Structure
{
    public function subject(): string;

    /**
     * @return (string|null)[]
     */
    public function groupNames(): array;

    public function groupsCount(): int;

    /**
     * @param string|int $nameOrIndex
     * @return bool
     */
    public function groupExists($nameOrIndex): bool;
}

Gdzie te abstrakcje na nulle? Domyślne parametry?

public static function match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int
    {
        $pr = Bug::fix($pattern);
        return Guard::invoke('preg_match', $pr, static function () use ($pr, $subject, &$matches, $flags, $offset) {
            return @\preg_match($pr, $subject, $matches, $flags, self::offset($subject, $offset)) ? 1 : 0;
        });
    }

p.s.

To pytanie retoryczne, zapewne nulle są w php uzasadnione.

Nie traktuje tego jako złośliwość - wyciągasz argument. Co prawda argument do autorytetu (czyli merytorycznie słaby), ale zawsze. Chętnie się odniosę.

Po pierwsze, w implementacji biblioteki null nie są stosowane nigdzie. Wszędzie znajdziesz odpowiednie obiekty. Nulle są tylko w tym miejscu gdzie wychodzą z biblioteki standardowej php i trzeba je obsłużyć, albo w interfejsem publicznym.

Pytanie teraz się pojawia - czemu są nulle w interfejsem publicznym? Otóż jak na pewno zauważyłeś, nulli nie ma praktycznie nigdzie, metoda first() zwraca match lub rzuca wyjątek, a findFirst() zwraca Optional. Tak Więc null jest unikany gdzie się tylko da.

Metoda którą znalazłeś służy do zwrócenia nazw grup, ale nie wszystkie grupy mają nazwy, więc jakoś trzeba to pokazać. Nie można w tym wypadku rzucić wyjątku, więc alternatywą dla nulla byłby tylko Optional, ale to w tym konkretnym miejscu nie miałoby sensu i utrudniłoby korzystanie z aplikacji. To byłby przypadek kiedy pragmatyzm bierze górę - ale nie dlatego że "bo tak", tylko w przypadku funkcji groupNames faktycznie zastanawialiśmy się nad wszystkimi możliwymi opcjami, i ustaliliśmy że null do oznaczenia grupy bez nazwy w funkcji zwracającej nazwy wszystkich grup to najmniejsze zło. Gdyby była jakąkolwiek sensowna alternatywa to na pewno bym ją wybrał, jednak takiej nie ma.

Także zapytasz pewnie jak to się ma do mojej wypowiedzi wcześniej - no podtrzymuje. Unikaj nulli gdzie tylko się da. Jeśli używasz zależności to odpowiednio je obsłuż. (Tutaj wstawię przykład z moją libką) jeśli wstawiasz interfejs DLA KOGOŚ, to wtedy nulle też powinny być unikane, chyba że nie ma standardowego sposobu żeby pokazać "brak czegoś", przy czym bardzo często jest.

Poza tym, błędem tutaj jest traktowanie kodu biblioteki jako przykład kodu który napisałbym w aplikacji, jednak biblioteka rządzi się swoimi prawami. Mogę jej implementację napisać jak chce, np strategiami, ale jednak interfejs ma być przede wszystkim dla użytkowników.

Dodatkowo, chciałbym zaznaczyć że nigdy nie używam null z automatu, i najpierw rozważam wszystkie inne opcje, wyjatek, obiekt, optional, strategie, coś innego. Dopiero kiedy uznam ze wszytkie opcje są średnie, wtedy no już jak nie ma jak tego żywcem zrobić, wtedy go użyje, i napisze komentarz czemu w tym miejscu został null użyty, bo szczerze wierzę że da się lepiej. Czyli podejście inne niż większość programistów, którzy mają tendencje wrzucać nulle i null-checki gdzie się da.

A ten drugi kod to kopia sygnatury z funkcji php, nie mogłem jej zmienić. Poza tym ta druga funkcja już leci do wrzutki, nie będzie jej w 1.0.

0

Czyli z jednej strony mówisz, że najlepiej nie używać nulli w ogóle i lepiej jak by ich nie było, a z drugiej strony sam podałeś przykład gdzie nulle mają sens i zastosowanie.

0
gajusz800 napisał(a):

Czyli z jednej strony mówisz, że najlepiej nie używać nulli w ogóle i lepiej jak by ich nie było, a z drugiej strony sam podałeś przykład gdzie nulle mają sens i zastosowanie.

Upraszczasz moją wypowiedź.

Cytuję:

Riddle napisał(a):

Unikaj nulli gdzie tylko się da. Jeśli używasz zależności to odpowiednio je obsłuż. (Tutaj wstawię przykład z moją libką) jeśli wstawiasz interfejs DLA KOGOŚ, to wtedy nulle też powinny być unikane, chyba że nie ma standardowego sposobu żeby pokazać "brak czegoś", przy czym bardzo często jest.

0

@Riddle: dlaczego Optional w tym konkretnym miejscu nie ma sensu? Grupa może mieć nazwę albo może jej nie mieć - wydaje się to być idealnym zastosowaniem dla Optionala.

0
bustard2 napisał(a):

@Riddle: dlaczego Optional w tym konkretnym miejscu nie ma sensu? Grupa może mieć nazwę albo może jej nie mieć - wydaje się to być idealnym zastosowaniem dla Optionala.

Dla pojedynczej grupy tak. Dla listy, raczej nie. List<Optional<String>> jest słabe. I tak na tym nic nie zrobisz oprócz checka isPresent().

0
Riddle napisał(a):
bustard2 napisał(a):

@Riddle: dlaczego Optional w tym konkretnym miejscu nie ma sensu? Grupa może mieć nazwę albo może jej nie mieć - wydaje się to być idealnym zastosowaniem dla Optionala.

Dla pojedynczej grupy tak. Dla listy, raczej nie. List<Optional<String>> jest słabe.

Jako pole struktury coś takiego nie ma sensu bo już lepiej mieć List<String>, ale jako jakaś wartośc przejściowa to czemu nie?

I tak na tym nic nie zrobisz oprócz checka isPresent().

A tu się nie zgadzam bo w Scali można zrobić groups.flatMap(opt => opt.toList) więc w vavr pewnie jakoś podobnie. I masz normalne List<String>

3

@Riddle:

  1. Nie mam nic przeciwko Twoim argumentom na temat pojawiania się nulla w T-Regx. Dla mnie to pragmatyzm. Jeśli nie chciałbym nulli, to pozostają języki, które tego nie wspierają.
    W tym przypadku pozostanie praktyczny problem znalezienia zasobów do realizacji projektu w takiej technologii ;)

Argumenty do autorytetu - nie wiem co tu masz na myśli, odniosłeś się do pytanie retorycznego, ok.

  1. Co do części Javowej.

Mikrooptymalizacja. Nie jesteś w stanie napisać kodu z nullami i potem przerobić na obiekty, tak żebyś w jakikolwiek sposób był w stanie to wykazać.
Odpal sobie profiler Javy i spróbować oszczędzić chociaż milisemundę w ten sposób. Good luck.

a) Napisałem o złożoności pamięciowej, gdzie masz 1 referencję na Optionala i 1 na obiekt, który Optional opakowuje, a 2>1.
Zamiast 100 stringów będziesz miał 100 optionali + 100 stringów -> więcej do odśmiecania. Nie zawsze będzie to robić różnicę i nie nie każdemu.

Nie rozumiem dlaczego piszesz o profilingu CPU, w tym kontekście? Różnica między złożonością obliczeniową, a pamięciową chyba jest jasna?

b) "Mikrooptymalizacja" - to odważny osąd, zwłaszcza, że nie został postawiany żaden konkretny problem ;) Czyli tak jakbyś mówił, że młotek jest zły w tym nieokreślonym przypadku, bo dobra praktyka to śrubokręt.

Tu nie musisz nic przerabiać, tylko wiedzieć jaki będzie wpływ dokonanego wyboru w kontekście konkretnego problemu. Dla zilustrowania prosty benchmark.
Dlaczego taki? Akurat mam sporo atrybutów w parsowanych XMLach, które to atrybuty są opcjonalne i przechowywanie tego jako Optional<> bądź dodatkowa abstrakcja znacznie pogarsza sytuację kontekście utylizacji zasobów systemowych.

Benchmark powinien pokazać tę różnicą w którą wątpisz. Różnicę na odchylenieu standardowym chyba też? To, że chcemy mieć mniejszy rozrzut chyba też oczywiste?
Czy dla każdego ta różnica będzie miała znacznie? Nie.

Jak to z benchmarkami, możesz przećwiczyć modyfikację parameterów i podzielić obserwacjami. Chętnie poczytam i zaktualizuje bazę wiedzy.

import org.openjdk.jmh.annotations.*;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

@BenchmarkMode({Mode.AverageTime})
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Benchmark)
public class FooBar {

    private static final int ITERATIONS = 10_000_000;

    @Benchmark
    public void testUsingNull() {
        for (int i = 0; i < ITERATIONS; i++) {
            String value = getValue(i);
            if (value != null) {
                value.toUpperCase();
            }
        }
    }

    @Benchmark
    public void testUsingOptional() {
        for (int i = 0; i < ITERATIONS; i++) {
            Optional<String> value = getOptionalValue(i);
            value.map(String::toUpperCase);
        }
    }

    public String getValue(int i) {
        return i % 2 == 0 ? null : new String("foobar");
    }

    public Optional<String> getOptionalValue(int i) {
        return i % 2 == 0 ? Optional.empty() : Optional.of(new String("foobar"));
    }

    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }
}
...
Benchmark                                                  Mode  Cnt          Score           Error   Units
FooBar.testUsingNull                                       avgt    5         81.149 ±         3.025   ms/op
FooBar.testUsingNull:·gc.alloc.rate                        avgt    5       1901.422 ±        51.298  MB/sec
FooBar.testUsingNull:·gc.alloc.rate.norm                   avgt    5  240004907.200 ±      4270.748    B/op
FooBar.testUsingNull:·gc.churn.G1_Eden_Space               avgt    5       1876.778 ±      1640.817  MB/sec
FooBar.testUsingNull:·gc.churn.G1_Eden_Space.norm          avgt    5  236945912.123 ± 208224566.756    B/op
FooBar.testUsingNull:·gc.churn.G1_Survivor_Space           avgt    5          0.307 ±         2.114  MB/sec
FooBar.testUsingNull:·gc.churn.G1_Survivor_Space.norm      avgt    5      38988.308 ±    269397.889    B/op
FooBar.testUsingNull:·gc.count                             avgt    5         12.000                  counts
FooBar.testUsingNull:·gc.time                              avgt    5         17.000                      ms
FooBar.testUsingOptional                                   avgt    5        104.823 ±        33.814   ms/op
FooBar.testUsingOptional:·gc.alloc.rate                    avgt    5       2726.277 ±       874.437  MB/sec
FooBar.testUsingOptional:·gc.alloc.rate.norm               avgt    5  440008861.556 ±      5026.051    B/op
FooBar.testUsingOptional:·gc.churn.G1_Eden_Space           avgt    5       2653.019 ±      1719.177  MB/sec
FooBar.testUsingOptional:·gc.churn.G1_Eden_Space.norm      avgt    5  428596860.742 ± 248892946.633    B/op
FooBar.testUsingOptional:·gc.churn.G1_Survivor_Space       avgt    5          0.011 ±         0.073  MB/sec
FooBar.testUsingOptional:·gc.churn.G1_Survivor_Space.norm  avgt    5       1929.930 ±     13408.112    B/op
FooBar.testUsingOptional:·gc.count                         avgt    5         17.000                  counts
FooBar.testUsingOptional:·gc.time                          avgt    5         16.000                      ms

I wycinek "ms na operację". Przy zwykłym null masz mniejsze czas średni i mniejsze odchylenie standardowe, czyli bardziej przewidywalne zachowanie.

Benchmark                                                  Mode  Cnt          Score           Error   Units
FooBar.testUsingNull                                       avgt    5         81.149 ±         3.025   ms/op
FooBar.testUsingOptional                                   avgt    5        104.823 ±        33.814   ms/op
1

A ten sam benchmark u mnie:

Benchmark                                                  Mode  Cnt          Score     Error   Units
optionalTest.FooBar.testUsingNull                          avgt    6         59.979 ±   7.387   ms/op
optionalTest.FooBar.testUsingNull:·gc.alloc.rate           avgt    6       1910.884 ± 233.982  MB/sec
optionalTest.FooBar.testUsingNull:·gc.alloc.rate.norm      avgt    6  120000004.927 ±   0.600    B/op
optionalTest.FooBar.testUsingNull:·gc.count                avgt    6        114.000            counts
optionalTest.FooBar.testUsingNull:·gc.time                 avgt    6        248.000                ms
optionalTest.FooBar.testUsingOptional                      avgt    6         60.851 ±  12.501   ms/op
optionalTest.FooBar.testUsingOptional:·gc.alloc.rate       avgt    6       1888.680 ± 376.343  MB/sec
optionalTest.FooBar.testUsingOptional:·gc.alloc.rate.norm  avgt    6  120000004.937 ±   1.016    B/op
optionalTest.FooBar.testUsingOptional:·gc.count            avgt    6         99.000            counts
optionalTest.FooBar.testUsingOptional:·gc.time             avgt    6        223.000                ms

I co teraz @yarel @Riddle ?

Jest prawie 1 ms różnicy. Chociaż niestesty w ramach błędu pomiaru...

0
jarekr000000 napisał(a):

A ten sam benchmark u mnie:

Benchmark                                                  Mode  Cnt          Score     Error   Units
optionalTest.FooBar.testUsingNull                          avgt    6         59.979 ±   7.387   ms/op
optionalTest.FooBar.testUsingNull:·gc.alloc.rate           avgt    6       1910.884 ± 233.982  MB/sec
optionalTest.FooBar.testUsingNull:·gc.alloc.rate.norm      avgt    6  120000004.927 ±   0.600    B/op
optionalTest.FooBar.testUsingNull:·gc.count                avgt    6        114.000            counts
optionalTest.FooBar.testUsingNull:·gc.time                 avgt    6        248.000                ms
optionalTest.FooBar.testUsingOptional                      avgt    6         60.851 ±  12.501   ms/op
optionalTest.FooBar.testUsingOptional:·gc.alloc.rate       avgt    6       1888.680 ± 376.343  MB/sec
optionalTest.FooBar.testUsingOptional:·gc.alloc.rate.norm  avgt    6  120000004.937 ±   1.016    B/op
optionalTest.FooBar.testUsingOptional:·gc.count            avgt    6         99.000            counts
optionalTest.FooBar.testUsingOptional:·gc.time             avgt    6        223.000                ms

I co teraz @yarel @Riddle ?

Jest prawie 1 ms różnicy. Chociaż niestesty w ramach błędu pomiaru...

No okej. Tylko że to jest 10 milionów operacji. Także w świetle danych dostarczonych przez @jarekr000000, faktycznie przyznaje że nie miałem racji jak napisałem że nie da się tego pokazać żadnym profilerem wyżej.

Jak widać, jak zamienimy 10 milionów Optional na 10 milionów nulli, to jesteśmy w stanie oszczędzić 1ms.

Także muszę cofnąć mój argument o mikrooptymalizacji.

0

@jarekr000000: gddzie tą mniejszą presję na allokator widzisz? patrzę na znormalizowany gc.alloc.rate (czyli .norm) , w Twoim przypadku średnie rozkładów są zbliżone (różnica na 2 miejscu po przecinku), zaś odchylenia standardowe różnią się nieco. Na wyniki patrzę przez pryzmat zmiennych losowych i ich rozkładów, a nie tylko przez wartości średnie.

Jeśli założymy rozkłady normalne (a takie zakłada JMH dla różnych statystyk) i przesuniemy je w kierunku zera (tak by skupić się na tej różnicy), to będziemy mieli 2 zmienne losowe:
X dla nulla, średnia = 0.927 i odchylenie = 0.6
Y dla optionala, średnia = 0.937 i odchylenie = 1.016

Jak sprawdzimy statystyczną różnicę P(|X-Y|>0.01) (tzn. że wyniki będą się różniły co najmniej o te 0.01) to wyjdzie 99%. Można sobie badać inne różnice, np. przez proste symulacje.

#!/usr/bin/env python3
import numpy as np

# Rozklady
mean_null = 0.927
std_dev_null = 0.6

mean_optional = 0.937
std_dev_optional = 1.016

# Próbki
n_simulations = 100000

x_samples = np.random.normal(mean_null, std_dev_null, n_simulations)
y_samples = np.random.normal(mean_optional, std_dev_optional, n_simulations)

# metryka
abs_diff = np.abs(x_samples - y_samples)
margin = 0.01

# prawdopodobienstwo
probability = np.mean(abs_diff > margin)

print(f"P(|X-Y| > {margin}): {probability}")

0
yarel napisał(a):
Riddle napisał(a):
ZrobieDobrze napisał(a):
Riddle napisał(a):

Weź nie gadaj bzdur.

Bzdurna jest rada "nie używaj nulla"

To nie jest bzdurna rada

To jest bzdurna rada ;)

Moim zdaniem wynika z dogmatyzmu i braku pragmatyzmu (typowe u ewangelistów różnych paradygmatów; z wiekiem przechodzi).

@yarel, jakbyś miał UoP na stanowisku: papież, też byś myślał dogmatami (i miał zawsze rację, dożywotnio)
https://pl.wikipedia.org/wiki/Dogmat_o_nieomylno%C5%9Bci_papie%C5%BCa

1 użytkowników online, w tym zalogowanych: 0, gości: 1