Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Bardzo duże tablice w Delphi - 10.000 elementów

leburaque 02 Lis 2011 22:34 1709 8
  • #1 02 Lis 2011 22:34
    leburaque
    Poziom 17  

    Witam. Wyliczam w swoim programie siły fizyczne działające na każdy z elementów kwadratowej matrycy. Matryca ma 100x100 elementów typu Real, ponieważ siły są liczbami typu rzeczywistego.

    Odkąd zadeklarowałem tablice:

    Kod: delphi
    Zaloguj się, aby zobaczyć kod


    z danymi w pamięci dzieją się dziwne rzeczy. Mianowicie wyjściowe dane z tablic zachodzą mi na siebie. Kompilator nie wskazuje mi błędów w deklaracjach, czy błędów w odwołaniach do tablic. Nie wysypuje mi też nigdzie, że mam za mało pamięci do takich operacji. A mimo tego, mimo, że nie nadpisuję jednych tablic drugimi w jednej z nich (matryca_ilosci) pojawiają mi się wyniki drugiej z nich (matryca_zywotnosci). Dzieje się tak od 55 do 155 elementu (choć nie wiem, czy to coś wniesie do dyskusji, podejrzewam, że 255-200 = 55, ale co to znaczy za Chiny Ludowe nie wiem).

    Jak mogę zbadać, czy rzeczywiście to kwestia pamięci, czy jakiegoś innego czynnika? Siedziałem nad kodem dłuższy czas, wygląda na to, że to tylko kwestia nowo zadeklarowanych tablic.

    Zastanawiam się, jak tę tablicę 100x100 obsługiwać w takim razie. W pliku? Rekordami w pliku? Czy w ogóle tablice tak duże mogą być przez Delphi obsługiwane?

    0 8
  • #2 03 Lis 2011 00:52
    McMonster
    Poziom 32  

    Pokaż cały kod. Moim zdaniem to jednak zwykły błąd programistyczny, posiedziałbym trochę z debuggerem, wtedy powinno się wyjaśnić.

    0
  • #4 03 Lis 2011 10:51
    leburaque
    Poziom 17  

    Naprawdę to takie małe tablice? Wydawało mi się, że to taka potężna struktura... 64 bity (tyle zajmuje w Delphi Real?) * 10.000 = 625 Kb. Wydawało mi się, że skoro to tylko część programu, to może być za dużo na obsługę i coś się krzaczy. Możecie mi w takim razie powiedzieć jak duże struktury mniej więcej bez kłopotu mogę obsługiwać w pamięci? Powiedzmy, że mogę przeliczyć jej wielkość - jaki jest możliwy limit deklaracji czegoś w pamięci?

    A co do kodu: mieliście rację Panowie - błąd był po mojej stronie.

    Kod był mniej więcej taki:
    - wczytywałem zmienne typu Byte z pliku tekstowego*
    - tymi zmiennymi wyznaczałem położenie w tablicy

    *błąd powstawał się podczas wczytywania zmiennych z pliku - tam gdzie z powodu różnych długości liczb w tekście (120 vs 5) podstawiałem błędne dane do zamiany tekstu na cyfrę: " 5" albo "120 ". Te spacje powodowały błędne przypisanie do zmiennej Byte i w efekcie zamiast max 100 otrzymywałem np 255... Taka głupia wpadka... :(

    To teraz prośba o rady. Rzecz w tym, że siedząc z kompilatorem nie wiedziałem gdzie w Delphi mogę znaleźć wartości zmiennych w trakcie wykonywania programu, więc ręcznie wysypywałem na ekran poszczególne wartości. Jest jakaś prostsza opcja pewnie, prawda?

    Dzięki temu nauczyłem się przez noc wykonywać operacje nie na tablicy a na pliku. Tu proszę o krytykę:

    zamiast tablicy

    Kod: delphi
    Zaloguj się, aby zobaczyć kod

    deklaruję

    Kod: delphi
    Zaloguj się, aby zobaczyć kod


    x i y w strukturze tabeli dawały mi położenie pola w matrycy, tu wystarczyło tylko przeliczyć y*maxx + x, ponieważ z dwuwymiarowej struktury musiałem utworzyć jednowymiarowy plik.
    Zapis i odczyt wykonuję za pomocą:
    Kod: delphi
    Zaloguj się, aby zobaczyć kod


    W efekcie - działa, ale zapis/odczyt zmiennych w ten sposób jest niezwykle powolny. Gdzie popełniam zasadniczy błąd? Czym się powinienem podczas zapisu/odczytu zainteresować?

    Bardzo dziękuję.
    Pozdrawiam

    0
  • Pomocny post
    #5 03 Lis 2011 16:09
    Luklukowaty
    Poziom 18  

    To po kolei:

    1) 600kB to nie jest dużo. Jeżeli Delphi tworzy kod 32-bitowy to masz do dyspozycji koło 2GB pamięci RAM, jeżeli 64-bit, to można śmiało stwierdzić, że dowolną ilość zainstalowaną w kompie
    2) odnośnie debugowania - ciekawy artykuł znaleziony dzięki google: http://4programmers.net/Delphi/Artyku%C5%82y/Debugowanie Wszystko jest ładnie w nim opisane
    3) w deklaracji pliku masz błąd - najpierw chcesz operować na typie Real, a później w pliku masz Integer - brak konsekwencji w działaniu.
    4) plik służy do przechowywania danych, a nie do operacji na tych danych - od tego masz pamięć RAM, która jest setki razy szybsza. Dodatkowo odczyt i zapis w sposób, który to zrobiłeś będzie jeszcze wolniejszy - jeżeli chcesz szybko odczytywać dane, to wykonujesz to poprzez BlockRead / BlockWrite, gdzie dostęp do pliku jest blokowy. Deklarujesz wtedy tablicę o wielkości np. 32kB i operujesz na takich partiach danych.

    0
  • Pomocny post
    #6 03 Lis 2011 17:48
    Dżyszla
    Poziom 42  

    1) Dla Delphi większej różnicy chyba nie ma, ale np w VC++ "duża" tablica zaczynała się bodajże faktycznie powyżej 300kB, gdzie praca na takich tablicach zmieniała sposób adresacji, a to skutkowało znacznym spowolnieniem działania programu. (dla ciekawych: załącznik) Jednakże, jak kolega wspomniał, teoretycznie możesz w środowisko 32-bitowym użyć i 2GB tablicy. W praktyce. Nie powinieneś mieć problemów z tablicą rzędu 1GB.
    2) Podczas debugowania: View/Watches, ewentualnie Project/Evaluate/modify. Możesz nawet zmieniać wartości zmiennych w trakcie działania programu przy użyciu ostatniego narzędzia!
    3 i 4 - jak poprzednik.

    0
    Załączniki:
  • #7 03 Lis 2011 19:37
    leburaque
    Poziom 17  

    Wow! Bardzo dziękuję za te informacje. Właśnie zagłębiam się w ten dział o Debuggowaniu. Przy okazji zagłębię się w http://www.dzyszla.aplus.pl/porada-13.html bo widzę tam kilka rzeczy o których albo nie miałem pojęcia, albo posługiwałem się nimi słabo...

    Tablice 1-2GB? Potężna struktura! Nie wiem, czy kiedykolwiek będę się takimi posługiwał, ale dobrze wiedzieć o tym.

    Tylko co do tej zmiany zmiennych - korzystam z wersji 10.0 i kiedy podczas wykonywania programu wybieram View->Debuging windows->Local variables albo Watches to dodaję zmienną do watches poprzez Run->Ad Watch. I wówczas widzę jakie wartości przyjmuje moja zmienna. O to chodziło, prawda? Ułatwi mi to pracę po 100kroć:D

    Co do tej niekonsekwencji Real-Integer, to akurat przepraszam za kolejny bubel w poście. Coś mi się pomieszało, jak pisałem kod z głowy:). Następnym razem po prostu wkleję kod.

    Odczyt blokami ogarnę jak przeczytam te informacje o Debbugerze. Po kolei, bo nie zapamiętam.

    Jeszcze przy okazji zapytam (nawet jeśli to głupie pytanie): po czym poznać, że Delphi tworzy 23 bitowy kod, albo 64? To zależy na jakim środowisku się kompiluje? Czy raczej ustawiam to w kompilatorze i od tego uzależniam środowisko wykonywania programu?

    I jeszcze o coś spytam Dżyszlę - tak odnośnie Twojego artykułu, który właśnie czytam: "Oczywiście, jeśli naszą aplikację chcemy udostępnić innym, to przed publikacją należy wszystkie zaznaczone opcje wyłączyć (tzw. kompilacja release)." Czy to znaczy, że przed udostępnieniem komuś programu odznaczyć wszystkie te opcje w kompilatorze? Co to zmieni dla użytkownika?

    Bardzo dziękuję Panowie! Naprawdę mi pomogliście! :)

    0
  • #8 12 Lis 2011 02:14
    postscripter8
    Poziom 8  

    Jeśli chodzi o ilość pamięci jaką można dysponować w programie, to faktycznie macie panowie rację. Myślę, że 1.5 GB jest bez problemu osiągalne. Ale jeśli chodzi o rozmiar tablicy, to nie byłbym taki pewien.

    O ile faktycznie jeden czy dwa megabajty nie powinny być problemem, o tyle tablica o rozmiarze rzędu gigabajta może nie zostać zaakceptowana przez system operacyjny, lub kompilator. Należy tu też rozróżnić tablice statyczne i dynamiczne. Te pierwsze są nadzorowane przez kompilator i jeśli mu się coś nie spodoba to najwyżej opluje jakimś "data segment too large", albo czymś podobnym.

    Tablice tworzone dynamicznie w czasie wykonania są już kłopotem systemu operacyjnego, do którego program zgłasza żądanie przydziału pamięci. Żądanie zbyt dużych ilości pamięci dla tablicy dynamicznej najpewniej skończy się wyjątkiem czasu wykonania i spektakularnym "kraszem".

    Powodem takich zachowań jest fakt, że tablica jest wektorem kolejnych bajtów w pamięci. Jeśli pamięć jest pofragmentowana i nie znajdzie się jeden wystarczająco duży 'chunk', to albo system uprzejmie posprząta bałagan i spróbuje wtedy wydzielić dostatecznie duży obszar, albo jeśli pamięci jest faktycznie za mało, bądź po prostu jest leniwy, to biednego programistę obrzuci wyjątkiem i się zwiesi.

    W przypadku stosowania dużych struktur danych prawie zawsze zasadne jest użycie struktur dynamicznych, takich jak listy. Jeśli obiektów ma być naprawdę dużo, to warto rozważyć użycie B-drzewa (dosyć kłopotliwe w implementacji), jeśli nie aż tak to tzw. talię (listę tablic).

    0
  • #9 13 Lis 2011 20:08
    Dżyszla
    Poziom 42  

    Odnośnie kodu - kompilator 64-bitowy (czyli dający kod wynikowy 64-bitowy, ale sam pracuje pod kontrolą 32 bitów) wprowadzony został dopiero w Delphi XE2 kilka miesięcy temu. I bardzo odradzam, bo tak zabugowanego środowiska to dawno nie było. Oczywiście aby stworzyć program 64-bitowy nie trzeba posiadać takowego systemu czy procesora. Oczywiście - wynikowego pliku się nie uruchomi wtedy.

    Odnośnie kompilacji "release" - kod jest po prostu mniejszy, lżejszy i bardziej optymalny. Oczywiście funkcjonalnie pozostaje taki sam cały czas. Ale działa szybciej i zajmuje mniej pamięci (tak dyskowej jak i operacyjnej). (to tak w dużym uproszczeniu).

    0