[C++] boolean array declareren, standaard waarde instellen?

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • frennek
  • Registratie: Augustus 2000
  • Laatst online: 27-05 12:03
Ik wil voor een programma een boolean array gebruiken, maar ik wil met 1 bepaalde standaard waarde beginnen voor de gehele array inhoud. Nu is dat wel op te lossen met een for loopje en alle waarden op false/true zetten, maar kan dat niet korter?

Acties:
  • 0 Henk 'm!

  • beany
  • Registratie: Juni 2001
  • Laatst online: 12:02

beany

Meeheheheheh

Kijk eens naar memset:

http://www.cplusplus.com/...brary/cstring/memset.html

edit: je kan ook opzoek in de c++ docs naar std::fill()

[ Voor 24% gewijzigd door beany op 05-12-2008 15:16 ]

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

C++:
1
2
bool *arr = new bool[100];
for(int i = 0; i < 100; i++) arr[i] = true;

werkt prima en is goed leesbaar. Voor performance zou je regel 2 kunnen vervangen met:
C++:
1
memset(arr, 0xFF, 100 * sizeof(bool));

-niks-


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ik zou std::fill gebruiken. Ik geloof dat "any nonzero value" gelijk moet staan aan true, maar in de praktijk geeft dit soms toch wat problemen, en een std::fill zet alle bools gewoon letterlijk op 'true' (1 dus, zoals het for-lusje zou doen), en niet op 0xff.

Overigens zou ik normaal sowieso een std::vector aanraden ipv een standaard array (kun je ook meteen de default waarde bij initialisatie instellen), maar helaas is een std::vector<bool> niet wat je denkt dat het is. Je zou evt. kunnen overwegen om gewoon een std::vector<char> te gebruiken.

C++:
1
std::vector<char> arr(100, true);

[ Voor 40% gewijzigd door .oisyn op 05-12-2008 15:58 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Qoning
  • Registratie: September 2007
  • Laatst online: 27-05 13:53
Een array in C++? Het kan wel maar als je toch bezig bent waarom dan niet gewoon

C++:
1
2
3
unsigned grootte = 100;
std::vector<bool> vec(grootte);
std::fill(vec.begin(), vec.end(), true);

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

Omdat std::vector<bool> niet is wat je denkt dat het is en imho beter vermeden kan worden. Daarnaast zou ik in dat geval nog altijd gebruik maken van de initialisatie-mogelijkheid van vector, zoals ik in mijn vorige voorbeeld liet zien. Dan is die hele fill overbodig.

[ Voor 49% gewijzigd door .oisyn op 05-12-2008 16:00 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Qoning
  • Registratie: September 2007
  • Laatst online: 27-05 13:53
.oisyn schreef op vrijdag 05 december 2008 @ 15:59:
Omdat std::vector<bool> niet is wat je denkt dat het is en imho beter vermeden kan worden. Daarnaast zou ik in dat geval nog altijd gebruik maken van de initialisatie-mogelijkheid van vector, zoals ik in mijn vorige voorbeeld liet zien. Dan is die hele fill overbodig.
oja dat was een speciale implementatie die 8 bools in een byte propt toch? nouja dan maak je er char van en dan is dat 'probleem' opgelost

Acties:
  • 0 Henk 'm!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
.oisyn schreef op vrijdag 05 december 2008 @ 15:56: maar helaas is een std::vector<bool> niet wat je denkt dat het is
std::deque<bool> heeft die problemen niet en kan in dit geval prima gebruikt worden :)

[ Voor 25% gewijzigd door PrisonerOfPain op 05-12-2008 16:18 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

Maar dat is tevens ook geen equivalent alternatief voor een array.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

een array is een array
een std::vector<T> is een namaak-array, die mogelijk resizable is
een std::vector<bool> is een apart geval
de topictitel beschrijft duidelijk een array :)

C definieert false gelijk aan nul, en true als niet gelijk aan nul, dus mijn memset van een paar posts hoger had net zo goed 0x01 ipv 0xFF kunnen zijn (of zelfs, true denk ik).

Maar om concreet antwoord te geven aan de TS, in C(++) is geheugen wat je alloceert danwel op je stack per definitie niet geinitialiseert, dus moet dat in code gedaan worden. Of je het dan zelf doet (met een for-loop of een memset) of voor je laat doen (met een STL container) maakt niet uit, gedaan moet het toch.
Het kan dus niet "korter" (het kost je hoe dan ook CPU cycles), tenzij je "korter" in code bedoelt, en in dat geval is een single-line for loop of een single-line memset (zoals hierboven door mij gegeven) toch vrij kort imho.

[ Voor 2% gewijzigd door MLM op 05-12-2008 16:55 . Reden: kromme zin ]

-niks-


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

MLM schreef op vrijdag 05 december 2008 @ 16:54:
een array is een array
een std::vector<T> is een namaak-array, die mogelijk resizable is
een std::vector<bool> is een apart geval
de topictitel beschrijft duidelijk een array :)
En de suggestie beschrijft duidelijk een vector. Mogen we niets meer suggereren? :)
C definieert false gelijk aan nul, en true als niet gelijk aan nul, dus mijn memset van een paar posts hoger had net zo goed 0x01 ipv 0xFF kunnen zijn (of zelfs, true denk ik).
Zoals ik al zei, de standaard doet dat idd, maar in de praktijk kun je problemen tegenkomen als bools niet exact gelijk zijn aan true. Ik ben ze tegengekomen, maar ik weet de exacte toedracht niet helemaal meer... kon zijn dat als je mijnBool == true deed, daar false uitkwam als de byte waarin mijnBool werd opgeslagen een waarde had die niet exact 'true' (1 dus) was, maar wel een andere waarde dan 0

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:02
De standaard zegt in ieder geval dat als een integer geïnterpreteert wordt als boolean, nul correspondeert met false en andere waarden met true, maar geldt dat ook voor een bitpatroon dat je in een boolean object kunt schrijven? Dat kan ik me eigenlijk niet voorstellen, want dat betekent dat de compiler er eigenlijk nooit vanuit kan gaan dat een boolean waarde in het geheugen genormaliseerd is naar 1/0 (of twee andere waarden) en dan worden alle vergelijkingen dus een stuk ingewikkelder (en minder efficient).

Je zou memset(1, array, sizeof(array)) kunnen gebruiken, maar eigenlijk moet je dan zeker weten dat true gerepresenteert wordt door 1 én dat booleans precies 1 byte groot zijn; dat laatste is zeker niet altijd het geval. (Ik gebruik zelf memset eigenlijk alleen om geheugen op nul te zetten, of om character arrays te vullen.)

Als je voor portability gaat, is een vector<char>(lengte,1) dus waarschijnlijk een beter idee, of gewoon je array toch handmatig vullen d.m.v. een for-lusje of std::fill, wat eigenlijk nauwelijks meer code is dan memset gebruiken.

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

boost::dynamic_bitset :)

Of een bool maken die default naar true construct.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
struct my_bool {
   my_bool() : value(true) {}
   my_bool(bool b) : value(b) {}

   operator bool () {return value;}

  ~my_bool() {}

   bool value;
};

std::vector<my_bool> ar;


Dan kan je ook meteen een std::vector gebruiken.

[ Voor 86% gewijzigd door Zoijar op 05-12-2008 21:30 ]


Acties:
  • 0 Henk 'm!

Anoniem: 21292

C++:
1
bool b[100]={false};

Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Daar dacht ik ook meteen aan. De compiler gebruikt voor zover ik weet de laatste waarde om de rest op te vullen. Ik weet echter niet zeker of dit in C, C++ of beide het geval is.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • Ghannes
  • Registratie: Oktober 2002
  • Laatst online: 05-06 10:09
Vul je dan niet enkel b[0]?

Acties:
  • 0 Henk 'm!

  • user109731
  • Registratie: Maart 2004
  • Niet online
Het eerste element word false (expliciet opgegeven), de rest word ook false (standaard waarde)

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Volgens mij zet hij ze gewoon op 0 (wat toevallig false is), dus dat werkt niet.

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 05-06 23:25
Soultaker schreef op vrijdag 05 december 2008 @ 18:13:
De standaard zegt in ieder geval dat als een integer geïnterpreteert wordt als boolean, nul correspondeert met false en andere waarden met true, maar geldt dat ook voor een bitpatroon dat je in een boolean object kunt schrijven?
Lijkt me wel eigenlijk, In dit geval:

code:
1
2
3
4
5
6
7
8
9
union T1
{
    struct T2
    {
        int32_t bitf1 : 16;
        int32_t bitf2 : 16;
    }bitval;
    int32_t intval;
};


Zou een bool( T1.intval ) altijd true moeten opleveren bij elke bitval.bitf1 of bitval.bitf2 die != 0.

[edit]
Owh wacht, dat was niet wat je bedoelde ..

[ Voor 14% gewijzigd door farlane op 06-12-2008 14:21 ]

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

.oisyn schreef op vrijdag 05 december 2008 @ 17:34:
[...]

Zoals ik al zei, de standaard doet dat idd, maar in de praktijk kun je problemen tegenkomen als bools niet exact gelijk zijn aan true. Ik ben ze tegengekomen, maar ik weet de exacte toedracht niet helemaal meer... kon zijn dat als je mijnBool == true deed, daar false uitkwam als de byte waarin mijnBool werd opgeslagen een waarde had die niet exact 'true' (1 dus) was, maar wel een andere waarde dan 0
Ik moet toegeven dat ik deze situatie nooit ben tegengekomen in de praktijk. Hoe dan ook heb je te maken met een compiler die zich niet aan de standaard houd. Mogelijk oplossing dan:
C:
1
memset(array, true, sizeof(bool) * items);

Tevreden? :P
quote: Soultaker
De standaard zegt in ieder geval dat als een integer geïnterpreteert wordt als boolean, nul correspondeert met false en andere waarden met true, maar geldt dat ook voor een bitpatroon dat je in een boolean object kunt schrijven? Dat kan ik me eigenlijk niet voorstellen, want dat betekent dat de compiler er eigenlijk nooit vanuit kan gaan dat een boolean waarde in het geheugen genormaliseerd is naar 1/0 (of twee andere waarden) en dan worden alle vergelijkingen dus een stuk ingewikkelder (en minder efficient).
Volgens mij gaat de compiler daar dan dus ook niet van uit :) Tevens is dit niet ingewikkelder, het is eerder een hardware optimalisatie die in C terecht gekomen is, je kan de zero flag van de processor gebruiken om te checken of een register true danwel false is :)

-niks-


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

.oisyn schreef op vrijdag 05 december 2008 @ 15:59:
Omdat std::vector<bool> niet is wat je denkt dat het is en imho beter vermeden kan worden. Daarnaast zou ik in dat geval nog altijd gebruik maken van de initialisatie-mogelijkheid van vector, zoals ik in mijn vorige voorbeeld liet zien. Dan is die hele fill overbodig.
Kan je dat eens uitleggen in mensentaal? B) (van die std::vector<bool>, waarom dat dat niet mag. Heb het al.

En een wrapper maken rond die booleans? Gaat dat? Een struct bijvoorbeeld?

[ Voor 18% gewijzigd door Snake op 06-12-2008 13:55 ]

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Snake schreef op zaterdag 06 december 2008 @ 13:52:
[...]

Kan je dat eens uitleggen in mensentaal? B) (van die std::vector<bool>, waarom dat dat niet mag. Heb het al.

En een wrapper maken rond die booleans? Gaat dat? Een struct bijvoorbeeld?
Dan heb je een struct waar je alsnog een memset/forloop/std::fill functie in moet maken. Dan verschuif je het "probleem" naar een andere plaats, waar je het dan alsnog moet oplossen :)

-niks-


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:02
MLM schreef op zaterdag 06 december 2008 @ 13:47:
Volgens mij gaat de compiler daar dan dus ook niet van uit :) Tevens is dit niet ingewikkelder, het is eerder een hardware optimalisatie die in C terecht gekomen is, je kan de zero flag van de processor gebruiken om te checken of een register true danwel false is :)
Nou ja, GCC lijkt in ieder geval wél van genormaliseerde waarden uit te gaan, en de gegenereerde code wordt daar toch echt wel simpeler van. Natuurlijk, als je één operand hebt dan kun je meestal gratis de zero flag bekijken om te bepalen of 'ie nul is, maar als je twee waarden wil vergelijken moet je meer werk doen:
C++:
1
2
int foo(bool a, bool b) { return (a != 0) == (b != 0); }
int bar(char a, char b) { return (a != 0) == (b != 0); }

Levert deze assembly op:
GAS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
foo:
        pushl   %ebp
        movl    %esp, %ebp
        movzbl  12(%ebp), %eax
        cmpb    %al, 8(%ebp)
        sete    %al
        movzbl  %al, %eax
        popl    %ebp
        ret

bar:
        pushl   %ebp
        movl    %esp, %ebp
        cmpb    $0, 8(%ebp)
        sete    %al
        cmpb    $0, 12(%ebp)
        popl    %ebp
        setne   %dl
        xorl    %edx, %eax
        movzbl  %al, %eax
        ret

Zoals je ziet is de code voor de tweede functie wel degelijk ingewikkelder.

Een ander voorbeeld:
C++:
1
2
3
4
5
6
7
#include <assert.h>
int main()
{
        bool foo[4];
        *(int*)foo = 0x01020304;
        assert(foo[1] == foo[2]);
}

Hoewel alle vier waarden non-zero zijn, zijn ze niet gelijk. memset() gebruiken is dus alleen een goed idee als je zeker weet dat het bitpatroon wat je gebruikt overeenkomt met wat de compiler gebruikt, en dus per definitie niet portable.
Mogelijk oplossing dan:
C:
1
memset(array, true, sizeof(bool) * items);
Dat werkt nog steeds niet met multi-byte bools; de compiler verwacht dan b.v. een 4-bytes lange waarde 1, maar je hebt 0x01010101 geschreven. Het probleem blijft dat memset() alleen bytes schrijft, en dat maakt die functie niet zo geschikt voor het vullen van elementen die groter zijn dan een byte (hoewel 0 en -1 nog wel werken).

Overigens kun je die true in de argumentlijst net zo goed vervangen door 1, want het argument van memset() is een int, en een conversie van true naar int levert per definitie 1 op, ook als de interne representatie anders is.

[ Voor 19% gewijzigd door Soultaker op 06-12-2008 15:46 ]


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Snake schreef op zaterdag 06 december 2008 @ 13:52:
En een wrapper maken rond die booleans? Gaat dat? Een struct bijvoorbeeld?
MLM schreef op zaterdag 06 december 2008 @ 14:05:
Dan heb je een struct waar je alsnog een memset/forloop/std::fill functie in moet maken. Dan verschuif je het "probleem" naar een andere plaats, waar je het dan alsnog moet oplossen :)
Soms heb ik echt het idee dat ik onzichtbaar ben hier :P

Verbeterde versie:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
template <typename T, T X = T()>
struct default_type { 
   default_type() : value(X) {} 
   default_type(T b) : value(b) {} 
   
   template <T Y>
   default_type(const default_type<T, Y>& src) : value(src.value) {}

   template <bool Y>
   default_type<T, X>& operator=(const default_type<T, Y>& src) {
        value = src.value;
        return *this;
   }

   operator T() {return value;} 

  ~default_type() {} 

   T value; 
}; 

typedef default_type<bool, true> default_true_bool;
typedef default_type<bool, false> default_false_bool;

int main() {
    default_true_bool ar1[10];
    default_false_bool ar2[10];

    for (int i=0; i<10; ++i) std::cout << std::boolalpha << ar1[i] << " ";
    std::cout << std::endl;

    for (int i=0; i<10; ++i) std::cout << std::boolalpha << ar2[i] << " ";
    std::cout << std::endl;

    return 0;
}


(haha, ok, genoeg... ach ja, nu heb je ook meteen default_type<int, 42> ;) Nu nog een template typedef... )

[ Voor 59% gewijzigd door Zoijar op 06-12-2008 17:20 ]


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Soultaker schreef op zaterdag 06 december 2008 @ 15:39:
[...]

Nou ja, GCC lijkt in ieder geval wél van genormaliseerde waarden uit te gaan, en de gegenereerde code wordt daar toch echt wel simpeler van. Natuurlijk, als je één operand hebt dan kun je meestal gratis de zero flag bekijken om te bepalen of 'ie nul is, maar als je twee waarden wil vergelijken moet je meer werk doen:
C++:
1
2
int foo(bool a, bool b) { return (a != 0) == (b != 0); }
int bar(char a, char b) { return (a != 0) == (b != 0); }

Levert deze assembly op:
GAS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
foo:
        pushl   %ebp
        movl    %esp, %ebp
        movzbl  12(%ebp), %eax
        cmpb    %al, 8(%ebp)
        sete    %al
        movzbl  %al, %eax
        popl    %ebp
        ret

bar:
        pushl   %ebp
        movl    %esp, %ebp
        cmpb    $0, 8(%ebp)
        sete    %al
        cmpb    $0, 12(%ebp)
        popl    %ebp
        setne   %dl
        xorl    %edx, %eax
        movzbl  %al, %eax
        ret

Zoals je ziet is de code voor de tweede functie wel degelijk ingewikkelder.
toon volledige bericht
Ik zie in jouw ASM helemaal nergens dat er getest word met een expliciete waarde (dat is toch het punt wat je wilt maken, dat bool geoptimized word op een specifieke waarde)

Anyway, voor compleetheid:
C++:
1
2
int foo(bool a, bool b) { return (a != 0) == (b != 0); } 
int bar(char a, char b) { return (a != 0) == (b != 0); }


Wat compiled tot (MS VS 2008, optimize)
GAS:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
foo:
    xor eax, eax
    cmp BYTE PTR _a$[esp-4], al
    setne   al
    xor ecx, ecx
    cmp BYTE PTR _b$[esp-4], cl
    setne   cl
    xor edx, edx
    cmp eax, ecx
    sete    dl
    mov eax, edx
    ret 0
bar:
    xor eax, eax
    cmp BYTE PTR _a$[esp-4], al
    setne   al
    xor ecx, ecx
    cmp BYTE PTR _b$[esp-4], cl
    setne   cl
    xor edx, edx
    cmp eax, ecx
    sete    dl
    mov eax, edx
    ret 0

ie: hetzelfde :) Dat is natuurlijk ook logisch omdat zowel een bool als een char 1 byte zijn, is de code hetzelfde (tests met 0)
Een ander voorbeeld:
C++:
1
2
3
4
5
6
7
#include <assert.h>
int main()
{
        bool foo[4];
        *(int*)foo = 0x01020304;
        assert(foo[1] == foo[2]);
}

Hoewel alle vier waarden non-zero zijn, zijn ze niet gelijk. memset() gebruiken is dus alleen een goed idee als je zeker weet dat het bitpatroon wat je gebruikt overeenkomt met wat de compiler gebruikt, en dus per definitie niet portable.
Je hebt gelijk. Maar zie dit:
C++:
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <assert.h>
int main()
{
    bool foo[4];
    *(int*)foo = 0x01020304;
    for(int i = 0; i < 4; i++)
        printf("%d: %s\n", i, foo[i] ? "true" : "false");
    assert(foo[1] && foo[2]);
}

Dit werkt zonder probleem bij mij, met alles "true"

Ik ben wel beniewd wat de standaard hier dan van maakt, dat ga ik zo even op zoeken
Dat werkt nog steeds niet met multi-byte bools; de compiler verwacht dan b.v. een 4-bytes lange waarde 1, maar je hebt 0x01010101 geschreven. Het probleem blijft dat memset() alleen bytes schrijft, en dat maakt die functie niet zo geschikt voor het vullen van elementen die groter zijn dan een byte (hoewel 0 en -1 nog wel werken).

Overigens kun je die true in de argumentlijst net zo goed vervangen door 1, want het argument van memset() is een int, en een conversie van true naar int levert per definitie 1 op, ook als de interne representatie anders is.
Toch zouden per definitie alle waarden niet gelijk aan nul als true moeten evalueren.

Ik heb een theorie dat op het moment dat de compiler als optimalisatie true en false wel degelijk vaste waarden geeft en als een bool niet 1 van die 2 waarden bevat, je undefinied behaviour krijgt, ONDANKS dat de standaard dat wel degelijk definieert :)

Meer tests met meer compilers gewenst...

-niks-


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

MLM schreef op zaterdag 06 december 2008 @ 18:20:
Ik zie in jouw ASM helemaal nergens dat er getest word met een expliciete waarde (dat is toch het punt wat je wilt maken, dat bool geoptimized word op een specifieke waarde)
Haha, GCC is vet slim ;) Hij kijkt gewoon of a == b in dat voorbeeld. Voor bools is (a != 0) == (b != 0) gelijk aan a == b. MSVC optimized toch wat minder. Ook zie je dat GCC niet meer kan optimizen als je chars gebruikt. Hij gebruikt dus iets van boolean folding op bools. Hij kijkt volgens mij ook alleen naar de laagste 8 bits. Dus in dat geval zou dan bv 0xFFFFFF00 == 0x0F0F0F00 == 0x0

[ Voor 9% gewijzigd door Zoijar op 06-12-2008 20:43 ]


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:02
MLM schreef op zaterdag 06 december 2008 @ 18:20:
Ik zie in jouw ASM helemaal nergens dat er getest word met een expliciete waarde (dat is toch het punt wat je wilt maken, dat bool geoptimized word op een specifieke waarde)
Je had wel gezien dat de assembly code verschillend is (simpeler voor de bools)? De compiler gaat er vanuit dat bools in het geheugen nul óf 1 zijn en de vergelijking met 0 dus overbodig is voor bools. Dat is toch wel een verschil, en het demonstreert mijn punt dat de compiler aanneemt dat bools in het geheugen maar een beperkt aantal geldige waarden mogen hebben (en dus niet alle waarden die een byte kan aannemen).
Maar zie dit:
C++:
1
2
3
4
5
6
7
8
9
10
#include <stdio.h> 
#include <assert.h> 
int main() 
{ 
    bool foo[4]; 
    *(int*)foo = 0x01020304; 
    for(int i = 0; i < 4; i++) 
        printf("%d: %s\n", i, foo[i] ? "true" : "false"); 
    assert(foo[1] && foo[2]); 
}

Dit werkt zonder probleem bij mij, met alles "true"
Met GCC werkt deze code sowieso niet goed vanwege aliasing problemen (mijn code had daar trouwens ook last van) maar los daarvan blijkt het van hetoptimalisatieniveau af te hangen of de assertion faalt (zonder optimalisaties triggert 'ie toevallig wél).

Dat het for-lusje wel werkt zegt eigenlijk niets over wat de compiler wel of niet aanneemt; natuurlijk zal de compiler de zero-flag gebruiken als die als side-effect al gezet is. Ook wil ik wel geloven dat Microsoft's compiler e.e.a. anders geïmplementeerd heeft, maar dat betekent ook niet dat je er vanuit mag gaan dat alle implementaties zo werken.

Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

obv test je maar 8 bits, je bool is immers 1 byte storage :P

Ik had ook een a == b test, die compiled inderdaad naar 3 instructies ofzo. Punt is, volgens mij gaan &&/|| altijd wel werken, maar een ==/!= niet omdat een compare cheaper is...

volgens mij zou een compiler die standaard C++ zou ondersteunen zoiets doen:
C:
1
2
bool a, b;
bool c = (a == b);

word
C:
1
bool c = (a != 0) ^ (b == 0);

maar doen GCC/MSVC eigenlijk
C:
1
bool c = ((char)a == (char)b);


ben niet zeker in hoeverre dit nou echt legaal C++ is :P

-niks-


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 19:02
Zoijar schreef op zaterdag 06 december 2008 @ 20:40:
Hij kijkt volgens mij ook alleen naar de laagste 8 bits. Dus in dat geval zou dan bv 0xFFFFFF00 == 0x0F0F0F00 == 0x0
Ja, maar op de geteste architectuur is bool ook maar 1 byte groot; de reden dat 'ie als argument 4 bytes groot lijkt is omdat de calling convention vereist dat argumenten op 4-byte-boundaries gealignd worden; de andere bytes zijn dus gegarandeerd nul (of misschien ongedefinieerd? weet niet precies hoe die calling convention gedefinieerd is).

edit:
Het enige wat ik er in de standaard over kan vinden is deze voetnoot:
42) Using a bool value in ways described by this International Standard as ‘‘undefined,’’ such as by examining the value of an uninitialized automatic variable, might cause it to behave as if it is neither true nor false.

[ Voor 23% gewijzigd door Soultaker op 06-12-2008 21:34 ]


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Ik denk dat het punt is, dat elke assignment aan type bool anders dan "true"/"false"/andere bool een conversie omvat (alle non-null values naar een 1). Op het moment dat ik memset met iets anders als een 1, is het mogelijk om deze behaviour the krijgen:
C++:
1
2
3
4
bool evil;
memset(&evil, 2, sizeof(bool));
bool b1 = evil == true; //false, immers 1 != 2
bool b2 = evil == false; //false, immers 0 != 2

het zou wel gewerkt hebben als je C++ de conversion laat doen:
C++:
1
bool evil = 2;


Aldus! opgelost! :P
memset is dus geen valide oplossing, maar werkt in de meeste gevallen wel... Heb ik in elk geval weer iets geleerd...

-niks-


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 08:54
Soultaker schreef op vrijdag 05 december 2008 @ 18:13:
De standaard zegt in ieder geval dat als een integer geïnterpreteert wordt als boolean, nul correspondeert met false en andere waarden met true, maar geldt dat ook voor een bitpatroon dat je in een boolean object kunt schrijven? Dat kan ik me eigenlijk niet voorstellen, want dat betekent dat de compiler er eigenlijk nooit vanuit kan gaan dat een boolean waarde in het geheugen genormaliseerd is naar 1/0 (of twee andere waarden) en dan worden alle vergelijkingen dus een stuk ingewikkelder (en minder efficient).

Je zou memset(1, array, sizeof(array)) kunnen gebruiken, maar eigenlijk moet je dan zeker weten dat true gerepresenteert wordt door 1 én dat booleans precies 1 byte groot zijn; dat laatste is zeker niet altijd het geval.
Klopt bijna helemaal. Je mag niet zomaar aannames doen over het bitpatroon van een bool, dus die memset(1,...) vind ik ook verdacht. Aan de andere kant is het wel een POD type, dus memcpy zou wel werken. Maar waarom prutsen? std::fill is inderdaad wat je zoekt. Helder, en de compiler kan naar hartelust optimaliseren.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op zondag 07 december 2008 @ 02:15:
std::fill is inderdaad wat je zoekt. Helder, en de compiler kan naar hartelust optimaliseren.
Een 'rep stos_' in veel gevallen met primitieven op x86. Heck, ik probeerde laatst voor een micro-optimalisatie een memcpy te implementeren met SSE intrinsics, had ie door dat ik alsnog gewoon geheugen aan het kopieren was dus maakte VC++ daar ook maar een rep movsd van 8)7

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

memcpy heeft zelf al een intrinsic form: http://msdn.microsoft.com/en-us/library/tzkfha43(VS.80).aspx
zolang je dus je compilerswitch "Use Intrinsics" aan hebt staan :)

dat zou stiekum gewoon bij jou het geval kunnen zijn als jouw SSE functie ook memcpy heet, dan gaat ie stiekum toch intrinsics doen en jouw functie negeren. het lijkt me sterk dat de compiler jouw SSE intrinsics gaat negeren en vervangen (dat lijkt me hoe dan ook niet de bedoeling)

-niks-


Acties:
  • 0 Henk 'm!

  • frennek
  • Registratie: Augustus 2000
  • Laatst online: 27-05 12:03
Jeetje, er is nog een hele discussie losgebarsten :D. Ik had het probleem eigenlijk al opgelost met memset en dit lijkt prima te werken. Voldoende voor mijn toepassing (programmeeropdracht voor studie).

Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 08:54
Docenten zijn meestal wat vergevingsgezinder dan professionals :)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

MSalters schreef op zondag 07 december 2008 @ 15:00:
Docenten zijn meestal wat vergevingsgezinder dan professionals :)
Zolang portability geen eis is, maakt het natuurlijk niet echt uit. En tbh, als ik portable code wil schrijven ga ik wel met een VM taal werken, dan hoef je niet/minder met platform bezig te zijn en meer met coden ;) Daarnaast, als ik de code van sommige "professionals" doorlees denk ik soms dat scholing best nuttig kan zijn :)

Alleen is het een ramp om een bool die niet true en niet false is te debuggen lijkt me :P

De enige reden dat ik C++ gebruik is omdat het de standaard is in mijn toekomstige tak van werk, het performance argument van C++ vind ik telkens minder op gaan in vergelijking met bijvoorbeeld C#. Daarnaast is C(++) 1 van de minder doorzichtige talen imo, er zijn heel veel dingen waar je op moet letten die in andere talen niet/nauwelijks aan de orde zijn (bijvoorbeeld, pointers en manual deallocation), er missen wel een aantal features (bijvoorbeeld, reflection of standaard ABI)

Maar goed, het geeft ook wel een soort gevoel van trots om best vloeiend in C++ te zijn :P

[ Voor 5% gewijzigd door MLM op 07-12-2008 16:26 ]

-niks-


Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 12:42

Sebazzz

3dp

MLM schreef op zondag 07 december 2008 @ 16:25:
[...]
(bijvoorbeeld, pointers en manual deallocation), er missen wel een aantal features (bijvoorbeeld, reflection of standaard ABI)
Volgens mijn C++ boek dat ik in de kast heb gegooid nadat ik begonnen ben met C# kan je auto_ptr gebruiken :+

Semi-offtopic: MSIL (de zooi die van de code overblijft na het compileren) is toch platform-onafhankelijk en niet 64-bit of 32-bit? Maw: Als je C++ libs oproept in de code maakt het toch niet uit of het 64-bit of 32-bit is (zolang het draaiend OS 64-bit is)?

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

MLM schreef op zondag 07 december 2008 @ 10:59:
memcpy heeft zelf al een intrinsic form: http://msdn.microsoft.com/en-us/library/tzkfha43(VS.80).aspx
zolang je dus je compilerswitch "Use Intrinsics" aan hebt staan :)
Dat is ook het idee. Een for-loop waarin je data van de ene array naar de ander kopiëert resulteert in een memcpy(), en de memcpy() resulteert weer in inline asm in de functie als het gebruik van intrinsics (voor die functie) aan staat.
dat zou stiekum gewoon bij jou het geval kunnen zijn als jouw SSE functie ook memcpy heet
Nee, mijn functie heette FastMemCopy()
het lijkt me sterk dat de compiler jouw SSE intrinsics gaat negeren en vervangen (dat lijkt me hoe dan ook niet de bedoeling)
Doet ie toch. Een __m128 assignen aan een andere __m128 gaat meestal via een movdqa instructie, die wilde ik dan ook gebruiken. Een hele array van die dingen resulteerde in een rep movsd. Ik moest inline assembly gebruiken om dat te voorkomen.
Sebazzz schreef op zondag 07 december 2008 @ 16:37:
Semi-offtopic: MSIL (de zooi die van de code overblijft na het compileren) is toch platform-onafhankelijk en niet 64-bit of 32-bit? Maw: Als je C++ libs oproept in de code maakt het toch niet uit of het 64-bit of 32-bit is (zolang het draaiend OS 64-bit is)?
De MSIL is idd platform-onafhankelijk, maar de C++ lib niet. En een assembly kan wel aangeven op welke platforms het gerund kan worden. Als je C++ lib dan bijv. 32 bits is, dan moet de assembly dat ook aangeven, anders crasht het keihard op een 64 bits OS.

[ Voor 27% gewijzigd door .oisyn op 07-12-2008 16:57 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 05-06 23:25
MLM schreef op zondag 07 december 2008 @ 16:25:
De enige reden dat ik C++ gebruik is omdat het de standaard is in mijn toekomstige tak van werk, het performance argument van C++ vind ik telkens minder op gaan in vergelijking met bijvoorbeeld C#.
Mja, het is een heel andere discussie natuurlijk, maar ik moet nog altijd een overtuigend bewijs zien. Mijn ervaring is dat zelfs VB6 de .Net talen eruit raced.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

.oisyn schreef op zondag 07 december 2008 @ 16:50:
[...]

Dat is ook het idee. Een for-loop waarin je data van de ene array naar de ander kopiëert resulteert in een memcpy(), en de memcpy() resulteert weer in inline asm in de functie als het gebruik van intrinsics (voor die functie) aan staat.


[...]

Nee, mijn functie heette FastMemCopy()


[...]

Doet ie toch. Een __m128 assignen aan een andere __m128 gaat meestal via een movdqa instructie, die wilde ik dan ook gebruiken. Een hele array van die dingen resulteerde in een rep movsd. Ik moest inline assembly gebruiken om dat te voorkomen.
toon volledige bericht
Okay, dat had ik niet verwacht. Ik dacht dat intrinsics eigenlijk gewoon assemblerinstructies waren waar de compiler wat context op had en dus kon reorderen/register allocaten, ik wist niet dat ie ze kon/mocht vertalen naar iets anders.
Ik weet wel dat sommige CRT's (die van Intel bijvoorbeeld) verschillende memcpy's heeft voor verschillende compilatiemodellen (verschillende SSE's), en daar zat volgens mij ook een SSE versie bij die 128 bits verplaatst
De MSIL is idd platform-onafhankelijk, maar de C++ lib niet. En een assembly kan wel aangeven op welke platforms het gerund kan worden. Als je C++ lib dan bijv. 32 bits is, dan moet de assembly dat ook aangeven, anders crasht het keihard op een 64 bits OS.
Voor zover ik weet krijg je een AssemblyNotFoundException als jouw MSIL een methode referenced (met DllImport attribute bijvoorbeeld) die niet van het juiste type is (MSIL, x86, x64) zodra je iets met de class gaat doen waar die methode aan hangt. Dat is in elk geval wat ik meegemaakt hebt met C# :)

-niks-


Acties:
  • 0 Henk 'm!

  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
MLM schreef op zondag 07 december 2008 @ 20:05:
[...]

Okay, dat had ik niet verwacht. Ik dacht dat intrinsics eigenlijk gewoon assemblerinstructies waren waar de compiler wat context op had en dus kon reorderen/register allocaten, ik wist niet dat ie ze kon/mocht vertalen naar iets anders.
Neen, af en toe wil de compiler nog wel eens (heel) vreemde dingen doen die eigenlijk helemaal niet nodig zijn. Waarschijnlijk omdat de compiler backend er nog overheen gaat; en die doet net wat meer als register allocation alleen.

Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

.oisyn schreef op zondag 07 december 2008 @ 16:50:

Nee, mijn functie heette FastMemCopy()
Scheelde dat veel? Zou het nog schelen om op die manier naar memory mapped GPU geheugen te copyen?

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

Het scheelde wel ja, maar ik heb slechts de invloed op m'n app gemeten, niet specifiek de tijd van de copy. Volgens mij maakt het voor write combined mem niet zoveel uit, omdat de MMU de writes toch wel queued en uiteindelijk in bursts uitschrijft. Je hebt natuurlijk wel iets minder overhead tov normale writes (je schrijft in een keer 16 bytes), maar ik weet niet hoe het zich verhoudt tov een rep movsd oid.

[ Voor 24% gewijzigd door .oisyn op 08-12-2008 13:24 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

.oisyn schreef op maandag 08 december 2008 @ 13:23:
Het scheelde wel ja, maar ik heb slechts de invloed op m'n app gemeten, niet specifiek de tijd van de copy. Volgens mij maakt het voor write combined mem niet zoveel uit, omdat de MMU de writes toch wel queued en uiteindelijk in bursts uitschrijft. Je hebt natuurlijk wel iets minder overhead tov normale writes (je schrijft in een keer 16 bytes), maar ik weet niet hoe het zich verhoudt tov een rep movsd oid.
Ik moet een redelijke hoeveelheid texture data vanuit shared memory uploaden (~10MB) en daar heb ik ongeveer 4ms voor. Nu haal ik met PBOs/memory mapped GPU geheugen een 2.5GB/s upload rate (pcie 1.1). Elke ms dat het sneller zou zijn is een grote performance gain. Ik Zou het eens kunnen proberen, maar ik weet niet of het uitmaakt. Volgens mij is toch de pcie bus de bottleneck.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 15:39

.oisyn

Moderator Devschuur®

Demotivational Speaker

Is het ook puur en alleen een copy van 1 lineaire buffer, of schrijf je ondertussen ook andere data naar ander mem weg? Want IIRC flushen de WC buffers bij een write naar niet-WC mem, en je hebt ook maar een gelimiteerd aantal WC buffers (bijv. 3 op Intel Netburst cpu's, dus als je dan 4 rijen pixels tegelijk schrijft dan veroorzaak je steeds onnodige flushes)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

.oisyn schreef op maandag 08 december 2008 @ 13:51:
Is het ook puur en alleen een copy van 1 lineaire buffer, of schrijf je ondertussen ook andere data naar ander mem weg? Want IIRC flushen de WC buffers bij een write naar niet-WC mem, en je hebt ook maar een gelimiteerd aantal WC buffers (bijv. 3 op Intel Netburst cpu's, dus als je dan 4 rijen pixels tegelijk schrijft dan veroorzaak je steeds onnodige flushes)
Nee, ik doe het heel straight-forward nu. Zoiets:

offset = 0
for n in nr_textures on GPU
pointer = map pbo for texture n
memcpy(pointer, shared_mem + offset, size texture n in bytes);
offset += size texture n

(en dan een stuk of 6 textures van ~1.5MB each)

[ Voor 3% gewijzigd door Zoijar op 08-12-2008 14:47 ]

Pagina: 1