Min kod tar för lång tid på sig, evig loop eller allmänt dålig programmering? (C++)

Permalänk

Min kod tar för lång tid på sig, evig loop eller allmänt dålig programmering? (C++)

Tjo alla! Sitter med en labb som ska vara inlämnad senast söndag och har fastnat. I alla tester jag kommer på att utföra så funkar koden som den ska (det är en vektorimplementation). Men när jag skickar upp koden för automatiskt granskning så svarar testprogrammet: "time limit exceeded".

Då det inte verkar finnas något sätt att få se hur testprogrammet (som heter kattis btw ^^) arbetar så finns det inget sätt att hitta felet genom att "debugga" eller liknande och jag klarar som sagt inte att återskapa felet.

Är det någon här som kan se var kattis fastnar?

EDIT: Jag har tagit bort koden härifrån då jag inte har hundra koll på vad läraren tycker om att jag lägger upp stora delar av labben på nätet!

För övrigt kom jag på att jag glömde säga tack till alla som hjälpte mig sist jag postade på denna forumdel så jag säger det nu; tack!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

Ditt största problem här är nog att du allokera och frigör minne alldeles för ofta.

Låt det allokerade antalet element skilja sig från antalet sparade element i vektorn och använd sedan någon form av expontentiell tillväxt. D.v.s. du har "capacity" och "size", och när "size > capacity" så låter du "capacity = size * 2".

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Skrivet av Weeblie:

Ditt största problem här är nog att du allokera och frigör minne alldeles för ofta.

Låt det allokerade antalet element skilja sig från antalet sparade element i vektorn och använd sedan någon form av expontentiell tillväxt. D.v.s. du har "capacity" och "size", och när "size > capacity" så låter du "capacity = size * 2".

Al'right, tack, ska testa det och se ifall det hjälper!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk

Hm, det gamla felet försvann men fick ett nytt fel istället som sammanfattas såhär:

Signal 6 is SIGABRT. This is generally either due to a failed assert() or due to an uncaught exception. A common exception is std::bad_alloc (which is thrown when memory allocation with new fails, which it does when you run out of memory).

Blir tokig på att jag inte får veta vilka tester den kör då mitt testsystem inte klagar alls.. Måste vara någonting speciellt jag missar.

EDIT: Jag har tagit bort koden härifrån då jag inte har hundra koll på vad läraren tycker om att jag lägger upp stora delar av labben på nätet!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

1. Du har glömt att kopiera över capacity i likamed-operatorn när du växer vektorn.
2. Din erase funktion gör inget vettigt.
3. Du bör placera koden för att växa vektorn i en egen funktion och även överväga krympningar (säg... när "size < capacity / 4").

ps. Att Kattis inte ger dig testen är rimligt då du inom "riktig" mjukvaru-utveckling sällan har möjlighet att i förväg få alla möjliga inputs som dina användare kommer att ge.

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Hedersmedlem

Det gör förmodligen inte någon jätteskillnad, men memcpy är vanligtvis effektivare än att kopiera varje element när man vill kopiera ett fält. Och är det verkligen meningen att använda bubble sort för sort-funktionen?

Permalänk
Medlem
Skrivet av Elgot:

Det gör förmodligen inte någon jätteskillnad, men memcpy är vanligtvis effektivare än att kopiera varje element när man vill kopiera ett fält. Och är det verkligen meningen att använda bubble sort för sort-funktionen?

Memcpy fungerar nog lite mindre bra. Tänk på att T kan vara något annat än Plain-Old-Data.

För att vara riktigt strikt med att duplicera beteendet hos STL's vector-klass bör man allokera minnet med en allokator som inte involverar default-konstruktorn. Sköt sedan om destruktioner genom att manuellt anropa destruktorn för varje element, och kopieringar med placement new. Lättast görs detta om man använder STL's allocator-klass.

Dock är detta rejält överkurs och inget som krävs för denna labb.

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Hedersmedlem

Weeblie: Memcpy och memmove måste ju fungera så länge inte containern måste bibehålla giltigheten i externa referenser.

Om syftet är att implementera om std::vector så måste destruktorn anropa elementens destruktorer för att undvika minnesläckor.

Något du också bör göra är att testa dina egna funktioner så att du ser att de verkligen ger rätt resultat, inte bara kör utan att krascha. Att Size och Capacity alltid har "rätt" värden t ex.

Du kan också implementera din insert så att den tar size - index operationer istället för size * 2. Detta kommer funka varje gång du inte behöva allokera om din vektor för att rymma ett nytt element. Du behöver bara flytta de element som ligger efter positionen du sätter in i.

Byt ut bubblesorten mot en riktigt sortering. Om de inte uttryckligen förbjudit stl::sort, så använd den, annars skulle jag rekommendera någon enklare nlogn-sortering annan än quicksort. Detta för att t ex heapsort är enklare att förstå än de varianter av quicksort som inte har n² som värsta fall. Tänk på att den som testar kan implementera T lite hur han vill, antingen genom att göra den jävlig att byta plats på (mycket data) eller genom att göra jämförelserna dyra (dyr operator==()).

Visa signatur

Religion och vidskepelse är smittsamma psykiska sjukdomar, den biologiska motsvarigheten till datorvirus.
"-Pappa, pappa, idag firade vi födelsedag och hela dagis fick gå på McDonalds. - Vems födelsedag då? - En farbror som hette Lenin."

Permalänk
Medlem
Skrivet av Ulvenstein:

Weeblie: Memcpy och memmove måste ju fungera så länge inte containern måste bibehålla giltigheten i externa referenser.

Därav min kommentar om "plain old data". POD hänvisar just till strukturer där alla fälten också är POD och kopieras rakt av.

Typiska exempel på sådant som inte är POD är STL's strängar, vektor-klassen själv, o.s.v.

Tumregeln är att om en klass täcks av "rule of three" (har en icke auto-generad copy-konstruktor, copy-tilldelning eller destruktor) så är den inte POD.

Skrivet av Ulvenstein:

Om syftet är att implementera om std::vector så måste destruktorn anropa elementens destruktorer för att undvika minnesläckor.

Det görs implicit av delete[].

Löjligt stavfel.
Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Skrivet av Weeblie:

1. Du har glömt att kopiera över capacity i likamed-operatorn när du växer vektorn.
2. Din erase funktion gör inget vettigt.
3. Du bör placera koden för att växa vektorn i en egen funktion och även överväga krympningar (säg... när "size < capacity / 4").

ps. Att Kattis inte ger dig testen är rimligt då du inom "riktig" mjukvaru-utveckling sällan har möjlighet att i förväg få alla möjliga inputs som dina användare kommer att ge.

Med dina och Gi][Gurras råd så har jag ändrat om lite. Men nu klagar Kattis tidigare än förut (test 4 istället för test 10 )

Så någon av mina ändringar har tydligen krånglat till det mer.

Btw... Är jävligt tacksam för all hjälp!

EDIT: Jag har tagit bort koden härifrån då jag inte har hundra koll på vad läraren tycker om att jag lägger upp stora delar av labben på nätet!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

Du är nästan framme nu!

Kika på fixCapacity. Kan du hitta det logiska felet på raden med copy_between_vec?

Testa annars på din egen dator:

vector<int> v1(1000); vector<int> v2; v1 = v2;

Det finns dessutom ett litet fel med tänket för erase.

Pröva med att skapa en vector med talen 0 till 9. Ta sedan bort ett av dem i mitten och skriv ut innehållet igen. Vad händer?

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Skrivet av Weeblie:

Du är nästan framme nu!

Kika på fixCapacity. Kan du hitta det logiska felet på raden med copy_between_vec?

Testa annars på din egen dator:

vector<int> v1(1000); vector<int> v2; v1 = v2;

Det finns dessutom ett litet fel med tänket för erase.

Pröva med att skapa en vector med talen 0 till 9. Ta sedan bort ett av dem i mitten och skriv ut innehållet igen. Vad händer?

Tack! Måste säga att jag är grymt imponerad på hur snabbt du ser fel som jag letat efter i timmar!

Tror jag fixat dem felen felen nu, men Kattis säger Signaled (11)

"Signal 11 är SIGSEGV, Segmentation Violation. Det här felet liknar SIGBUS mycket. Det betyder att programmet har försökt komma åt minne som det inte hade tillgång till, antingen för att processen inte hade mappat in minnet, eller för att den saknade rättigheter. Kontrollera att allting är initialiserat, var försiktig med pekare i allmänhet och null-pekare i synnerhet."

Några ideer?

EDIT: Jag har tagit bort koden härifrån då jag inte har hundra koll på vad läraren tycker om att jag lägger upp stora delar av labben på nätet!

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem

Okay... problemet är att du (ibland) förändrar vSize innan du anropar fixCapacity.

Försök att skissa upp på papper och penna över vad som då händer.

Jag föreslår att du försöker vara lite mer konsekevent här och följer något i stil med...

Vid tillväxt:

1. Anropar fixCapacity.
2. Stoppar in element/möblerar om.
3. Sätter vSize.

Vid krympning:

1. Tar bort element/möblerar om.
3. Sätter vSize.
3. Anropar fixCapacity.

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Skrivet av Weeblie:

Okay... problemet är att du (ibland) förändrar vSize innan du anropar fixCapacity.

Försök att skissa upp på papper och penna över vad som då händer.

Jag föreslår att du försöker vara lite mer konsekevent här och följer något i stil med...

Vid tillväxt:

1. Anropar fixCapacity.
2. Stoppar in element/möblerar om.
3. Sätter vSize.

Vid krympning:

1. Tar bort element/möblerar om.
3. Sätter vSize.
3. Anropar fixCapacity.

Ahh. Jag hittade det! *lycklig*

Tack så enormt mycket för hjälpen, du lyckades just ge mig 2 poäng till tentan.

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Hedersmedlem
Skrivet av Weeblie:

Därav min kommentar om "plain old data". POD hänvisar just till strukturer där alla fälten också är POD och kopieras rakt av.

Typiska exempel på sådant som inte är POD är STL's strängar, vektor-klassen själv, o.s.v.

Rumregeln är att om en klass täcks av "rule of three" (har en icke auto-generad copy-konstruktor, copy-tilldelning eller destruktor) så är den inte POD.

Det görs implicit av delete[].

Ah, läste lite slarvigt där.

Ang. POD så krävs det bra mycket mer än så för att en datastruktur inte ska kunna flyttas. Strukturen måste ju isåfall veta var den låg från början, något som jag inte kan se hindras av rule of three. ska se om jag giter testa att flytta på STL-datastrukturer med våld någon dag. Pekare till heap-allokerad data behöver ju inte veta var de ligger.

Visa signatur

Religion och vidskepelse är smittsamma psykiska sjukdomar, den biologiska motsvarigheten till datorvirus.
"-Pappa, pappa, idag firade vi födelsedag och hela dagis fick gå på McDonalds. - Vems födelsedag då? - En farbror som hette Lenin."

Permalänk
Medlem
Skrivet av Ulvenstein:

Ah, läste lite slarvigt där.

Ang. POD så krävs det bra mycket mer än så för att en datastruktur inte ska kunna flyttas. Strukturen måste ju isåfall veta var den låg från början, något som jag inte kan se hindras av rule of three. ska se om jag giter testa att flytta på STL-datastrukturer med våld någon dag. Pekare till heap-allokerad data behöver ju inte veta var de ligger.

Självklart behöver inte ett objekt vara POD för att vara flyttbart med memcpy/memmove; men jag tror att du har underskattat kraven här.

Ta t.ex. sträng-objekt. Kan du garantera att det internt allokerade minnet inte frigörs flera gånger?

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Skrivet av Weeblie:

3. Du bör placera koden för att växa vektorn i en egen funktion och även överväga krympningar (säg... när "size < capacity / 4").

Får man verkligen krympa vektorn? Jag trodde att ifall man anropar reserve() och sen inte går över den storleken så är man garanterad att elementen inte flyttar på sig (förutom de som kommer efter element man gör erase() på såklart).

Visa signatur

Dator: i5-13600K, Asus Prime Z690-P, Noctua NH-D14, Kingston Fury Beast RGB 32GB DDR5-6000, Gigabyte RTX 4090 gaming OC, Seasonic Platinum SS-1000XP, Lian-Li Lancool 215, Samsung 980Pro 2TB M.2 NVME, Acer Predator XB323QKNV 4k 144Hz

Permalänk
Hedersmedlem
Skrivet av Weeblie:

Självklart behöver inte ett objekt vara POD för att vara flyttbart med memcpy/memmove; men jag tror att du har underskattat kraven här.

Ta t.ex. sträng-objekt. Kan du garantera att det internt allokerade minnet inte frigörs flera gånger?

Jag är uppriktigt nyfiken på hur det skulle kunna gå till, förutsatt att man inte sitter i ett system med garbage collecting. Det krävs ju att det någonstans finns information om att objektet flyttats, och att denna information skulle kunna leda till att minnet i andra ändan av pekarna skulle frigöras flera gånger.

Visa signatur

Religion och vidskepelse är smittsamma psykiska sjukdomar, den biologiska motsvarigheten till datorvirus.
"-Pappa, pappa, idag firade vi födelsedag och hela dagis fick gå på McDonalds. - Vems födelsedag då? - En farbror som hette Lenin."

Permalänk
Medlem

[QUOTE=Milky Way;10405812]Får man verkligen krympa vektorn? Jag trodde att ifall man anropar reserve() och sen inte går över den storleken så är man garanterad att elementen inte flyttar på sig (förutom de som kommer efter element man gör erase() på såklart).[/QUOTE]

Du har rätt i att standarden egentligen förbjuder en från att krympa vektorn när man tar bort element från den. Inte ens ett clear-anrop skall frigöra minne.

Det är inte nödvändigt att anropa reserve för att få detta beteende.

Skrivet av Ulvenstein:

Jag är uppriktigt nyfiken på hur det skulle kunna gå till, förutsatt att man inte sitter i ett system med garbage collecting. Det krävs ju att det någonstans finns information om att objektet flyttats, och att denna information skulle kunna leda till att minnet i andra ändan av pekarna skulle frigöras flera gånger.

Sköt om konstruktion och destruktion manuellt.

#include <cstdlib> #include <memory> #include <iostream> using namespace std; class Foobar { public: // Märk att vi inte har någon default-konstruktor. Foobar(int x) : x(x) { } Foobar(const Foobar& other) : x(other.x) { } Foobar& operator=(const Foobar& other) { x = other.x; } ~Foobar() { x = 0; } public: int x; }; int main() { Foobar obj1(1); Foobar obj2(2); size_t capacity = 100; Foobar* v = nullptr; // Placement new + Destruktor. v = static_cast<Foobar*>(malloc(sizeof(Foobar) * capacity)); cout << "Metod 1:" << endl; cout << v[0].x << " " << v[1].x << endl; new (&v[0]) Foobar (obj1); new (&v[1]) Foobar (obj2); cout << v[0].x << " " << v[1].x << endl; v[1].~Foobar(); v[0].~Foobar(); cout << v[0].x << " " << v[1].x << endl; free(v); // Enklare och bättre alternativ med std::allocator. allocator<Foobar> alloc; v = alloc.allocate(capacity); cout << endl << "Metod 2:" << endl; cout << v[0].x << " " << v[1].x << endl; alloc.construct(&v[0], obj1); alloc.construct(&v[1], obj2); cout << v[0].x << " " << v[1].x << endl; alloc.destroy(&v[1]); alloc.destroy(&v[0]); cout << v[0].x << " " << v[1].x << endl; alloc.deallocate(v, capacity); }

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."

Permalänk
Skrivet av Weeblie:

Du har rätt i att standarden egentligen förbjuder en från att krympa vektorn när man tar bort element från den. Inte ens ett clear-anrop skall frigöra minne.

Finns det någon vettig anledning till detta? Att frigöra minne som inte behövs borde väl vara att föredra? Enda anledningen jag kan komma på är väl att allokera och sen deallokera minne ofta borde fragmentera heapen men det borde väl inte vara något större problem?

Visa signatur

Avatarkreds till: http://imgur.com/HOxIL
Alakai säger: Ryssen skrattar. Norrland hembränner på uppdrag av regeringen. Sälar dör i blyförgiftning, fulla och glada. Förvirringen är total. Kungen är nöjd.

Permalänk
Medlem
Skrivet av Mikael_Berglund:

Finns det någon vettig anledning till detta? Att frigöra minne som inte behövs borde väl vara att föredra? Enda anledningen jag kan komma på är väl att allokera och sen deallokera minne ofta borde fragmentera heapen men det borde väl inte vara något större problem?

Det är svårt att säga exakt varför standarden är som den är; men ett troligt svar är att man inte vill göra "vector" onödigt komplicerad bara för att kunna stödja "reserve". D.v.s. emulera klassiskt "array + räknare"-beteende.

Visa signatur

"Nothing is impossible because impossible itself says I M Possible..."