maximum cases in a switch ?

Discussion in 'C++' started by Lynn McGuire, Aug 9, 2011.

  1. Lynn McGuire

    Lynn McGuire Guest

    Is there a maximum number of cases in a switch ?
    I have a class with a method that now has 525
    cases in a switch and will probably double this
    over the next couple of years.

    Thanks,
    Lynn
     
    Lynn McGuire, Aug 9, 2011
    #1
    1. Advertising

  2. Lynn McGuire

    LR Guest

    Lynn McGuire wrote:
    > Is there a maximum number of cases in a switch ?
    > I have a class with a method that now has 525
    > cases in a switch and will probably double this
    > over the next couple of years.
    >
    > Thanks,
    > Lynn


    According to n3242,

    "Case labels for a switch statement (excluding those for any nested
    switch statements) [16 384]."
     
    LR, Aug 9, 2011
    #2
    1. Advertising

  3. Lynn McGuire <> wrote:
    > Is there a maximum number of cases in a switch ?
    > I have a class with a method that now has 525
    > cases in a switch and will probably double this
    > over the next couple of years.


    I can't find any stated upper limit in the standard. There probably
    isn't (except for the maximum value of the numerical type being used,
    of course). Individual compilers might impose a limit based on the
    memory usage that parsing a switch statement requires, but my guess
    is that this limit is probably very large.
     
    Juha Nieminen, Aug 9, 2011
    #3
  4. Lynn McGuire

    Lynn McGuire Guest

    On 8/9/2011 11:27 AM, LR wrote:
    > Lynn McGuire wrote:
    >> Is there a maximum number of cases in a switch ?
    >> I have a class with a method that now has 525
    >> cases in a switch and will probably double this
    >> over the next couple of years.
    >>
    >> Thanks,
    >> Lynn

    >
    > According to n3242,
    >
    > "Case labels for a switch statement (excluding those for any nested
    > switch statements) [16 384]."


    A short int, cool ! Gives me plenty of room
    for additions.

    Thanks,
    Lynn
     
    Lynn McGuire, Aug 9, 2011
    #4
  5. On 8/9/2011 12:03 PM, Lynn McGuire wrote:
    > Is there a maximum number of cases in a switch ?
    > I have a class with a method that now has 525
    > cases in a switch and will probably double this
    > over the next couple of years.


    You have your answer, I'm not going to repeat it. Remember that it's
    not normative, though.

    I have a different question. How easy do you think it is going to be to
    maintain the code with more than 1000 cases in a single switch
    statement? How easy is it to debug? Wouldn't it make sense, perhaps,
    to categorize your values and call different functions based on, say,
    ranges first and do further branching later in those functions, maybe?
    OK, OK, not /a/ question, but three.

    And a slightly different approach would be to implement handling of
    those ranges in an external module (dynamically loaded library, or
    "shared object" as they are known in another unixverse) which will
    register the range (or ranges) of values which it serves with its
    function, pointers to which you should keep while the module is
    loaded... Just thought I'd suggest that, maybe you find it useful.

    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Aug 9, 2011
    #5
  6. Lynn McGuire

    Lynn McGuire Guest

    On 8/9/2011 2:42 PM, Victor Bazarov wrote:
    > On 8/9/2011 12:03 PM, Lynn McGuire wrote:
    >> Is there a maximum number of cases in a switch ?
    >> I have a class with a method that now has 525
    >> cases in a switch and will probably double this
    >> over the next couple of years.

    >
    > You have your answer, I'm not going to repeat it. Remember that it's not normative, though.
    >
    > I have a different question. How easy do you think it is going to be to maintain the code with more than 1000 cases in a single
    > switch statement? How easy is it to debug? Wouldn't it make sense, perhaps, to categorize your values and call different functions
    > based on, say, ranges first and do further branching later in those functions, maybe? OK, OK, not /a/ question, but three.
    >
    > And a slightly different approach would be to implement handling of those ranges in an external module (dynamically loaded library,
    > or "shared object" as they are known in another unixverse) which will register the range (or ranges) of values which it serves with
    > its function, pointers to which you should keep while the module is loaded... Just thought I'd suggest that, maybe you find it
    > useful.
    >
    > V


    Incredibly easy. Here is some of the method:

    DataDescriptor aDD_GEN_CALVAPPRE (GEN_CALVAPPRE, "CALVAPPRE",
    "Calculate Vapor Pressure for all streams", true, false, false,
    SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    SYM_DGenInit, 420, nil, nil);

    DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    {
    switch (aSymbol)
    {
    case GEN_CALVAPPRE:
    return & aDD_GEN_CALVAPPRE;
    case GEN_CALSONVEL:
    return & aDD_GEN_CALSONVEL;
    case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    ....
    }

    // default return
    return NULL;
    }

    This is a 470,000 line C++ Win32 user interface for a highly
    successful calculation engine. This code is bulletproof and
    very extensible. This particular code was actually converted
    from Smalltalk to C++ about 10 years ago.

    Lynn
     
    Lynn McGuire, Aug 9, 2011
    #6
  7. > Incredibly easy. Here is some of the method:
    >
    > DataDescriptor aDD_GEN_CALVAPPRE (GEN_CALVAPPRE, "CALVAPPRE",
    > "Calculate Vapor Pressure for all streams", true, false, false,
    > SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    > SYM_DGenInit, 420, nil, nil);
    >
    > DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    > {
    > switch (aSymbol)
    > {
    > case GEN_CALVAPPRE:
    > return & aDD_GEN_CALVAPPRE;
    > case GEN_CALSONVEL:
    > return & aDD_GEN_CALSONVEL;
    > case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    > return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    > ...
    > }
    >
    > // default return
    > return NULL;
    > }
    >
    > This is a 470,000 line C++ Win32 user interface for a highly
    > successful calculation engine. This code is bulletproof and
    > very extensible. This particular code was actually converted
    > from Smalltalk to C++ about 10 years ago.
    >
    > Lynn


    Oh my God.

    You are laughing, isn't it ?

    There is nothing about anything named calvappre on the internet.
     
    Leo Equinox Gaspard, Aug 9, 2011
    #7
  8. Lynn McGuire

    Lynn McGuire Guest

    On 8/9/2011 4:39 PM, Leo Equinox Gaspard wrote:
    >> Incredibly easy. Here is some of the method:
    >>
    >> DataDescriptor aDD_GEN_CALVAPPRE (GEN_CALVAPPRE, "CALVAPPRE",
    >> "Calculate Vapor Pressure for all streams", true, false, false,
    >> SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    >> SYM_DGenInit, 420, nil, nil);
    >>
    >> DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    >> {
    >> switch (aSymbol)
    >> {
    >> case GEN_CALVAPPRE:
    >> return & aDD_GEN_CALVAPPRE;
    >> case GEN_CALSONVEL:
    >> return & aDD_GEN_CALSONVEL;
    >> case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    >> return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    >> ...
    >> }
    >>
    >> // default return
    >> return NULL;
    >> }
    >>
    >> This is a 470,000 line C++ Win32 user interface for a highly
    >> successful calculation engine. This code is bulletproof and
    >> very extensible. This particular code was actually converted
    >> from Smalltalk to C++ about 10 years ago.
    >>
    >> Lynn

    >
    > Oh my God.
    >
    > You are laughing, isn't it ?
    >
    > There is nothing about anything named calvappre on the internet.


    Try googling "cal vap pre". That is shorthand
    in our internal engineering software language
    for a specific calculation.

    Lynn
     
    Lynn McGuire, Aug 9, 2011
    #8
  9. On 8/9/2011 4:39 PM, Lynn McGuire wrote:
    > On 8/9/2011 2:42 PM, Victor Bazarov wrote:
    >> On 8/9/2011 12:03 PM, Lynn McGuire wrote:
    >>> Is there a maximum number of cases in a switch ?
    >>> I have a class with a method that now has 525
    >>> cases in a switch and will probably double this
    >>> over the next couple of years.

    >>
    >> You have your answer, I'm not going to repeat it. Remember that it's
    >> not normative, though.
    >>
    >> I have a different question. How easy do you think it is going to be
    >> to maintain the code with more than 1000 cases in a single
    >> switch statement? How easy is it to debug? Wouldn't it make sense,
    >> perhaps, to categorize your values and call different functions
    >> based on, say, ranges first and do further branching later in those
    >> functions, maybe? OK, OK, not /a/ question, but three.
    >>
    >> And a slightly different approach would be to implement handling of
    >> those ranges in an external module (dynamically loaded library,
    >> or "shared object" as they are known in another unixverse) which will
    >> register the range (or ranges) of values which it serves with
    >> its function, pointers to which you should keep while the module is
    >> loaded... Just thought I'd suggest that, maybe you find it
    >> useful.
    >>
    >> V

    >
    > Incredibly easy. Here is some of the method:
    >
    > DataDescriptor aDD_GEN_CALVAPPRE (GEN_CALVAPPRE, "CALVAPPRE",
    > "Calculate Vapor Pressure for all streams", true, false, false,
    > SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    > SYM_DGenInit, 420, nil, nil);
    >
    > DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    > {
    > switch (aSymbol)
    > {
    > case GEN_CALVAPPRE:
    > return & aDD_GEN_CALVAPPRE;
    > case GEN_CALSONVEL:
    > return & aDD_GEN_CALSONVEL;
    > case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    > return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;


    Ugh! You can do it with a macro

    #define returnDDfor(A) case A: return & aDD_ ## A

    and

    returnDDfor(GEN_CALVAPPRE);
    returnDDfor(GEN_CALSONVEL);

    At least you need to type it only once...

    I wonder, have you tried looking at the generated code? Does the
    compiler have a jump table or does it create a bunch of 'if-then goto'
    parts? The latter gets expensive for the case labels at the bottom of
    the switch statement.

    > ...
    > }
    >
    > // default return
    > return NULL;
    > }
    >
    > This is a 470,000 line C++ Win32 user interface for a highly
    > successful calculation engine. This code is bulletproof and
    > very extensible. This particular code was actually converted
    > from Smalltalk to C++ about 10 years ago.
    >
    > Lynn


    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Aug 10, 2011
    #9
  10. Victor Bazarov <> wrote:
    > I wonder, have you tried looking at the generated code? Does the
    > compiler have a jump table or does it create a bunch of 'if-then goto'
    > parts? The latter gets expensive for the case labels at the bottom of
    > the switch statement.


    One can generally assume that modern compilers are decently good at
    optimizing large switch blocks. (If a specific compiler isn't, chances
    are that it isn't good at optimizing anything else either.)
     
    Juha Nieminen, Aug 10, 2011
    #10
  11. Lynn McGuire wrote:
    > Incredibly easy. Here is some of the method:
    >
    > DataDescriptor aDD_GEN_CALVAPPRE (GEN_CALVAPPRE, "CALVAPPRE",
    > "Calculate Vapor Pressure for all streams", true, false,
    > false,
    > SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    > SYM_DGenInit, 420, nil, nil);
    >
    > DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    > {
    > switch (aSymbol)
    > {
    > case GEN_CALVAPPRE:
    > return & aDD_GEN_CALVAPPRE;
    > case GEN_CALSONVEL:
    > return & aDD_GEN_CALSONVEL;
    > case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    > return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    > ...
    > }
    >
    > // default return
    > return NULL;
    > }


    I would strongly recommend a meta data table.

    static const DataDescriptor DispatchTable[] =
    { DataDescriptor(GEN_CALVAPPRE, "CALVAPPRE",
    "Calculate Vapor Pressure for all streams", true, false, false,
    SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    SYM_DGenInit, 420, nil, nil),
    DataDescriptor(GEN_CALSONVEL, ...),
    ...
    };

    If the DataDescriptors are ordered by the key aSymbol, you could use a
    binary search to locate a DataDescriptor in the table.

    Alternatively class DataDescriptor could hold a static repository of
    type std::map<int,DataDescriptor*> with all it's instances. The
    constructor of DataDescriptor could register it's own instance in the
    repository. Then the method GenGroup::descriptor reduces to a hash
    lookup in the repository. No more than a dozen code lines instead of
    some thousands in the switch case. And the advantage is that you can
    keep the code for the DataDescriptor definitions without changes. You
    only have to delete the large switch.

    > This is a 470,000 line C++ Win32 user interface for a highly
    > successful calculation engine.


    Maybe, but keep in mind that compilers are not well tested with this
    kind of code. I triggered internal compiler errors with too large
    functions from time to time (large window procedures).

    > This code is bulletproof and
    > very extensible.


    The two above solutions too.


    Marcel
     
    Marcel Müller, Aug 10, 2011
    #11
  12. Marcel Müller <> wrote:
    > If the DataDescriptors are ordered by the key aSymbol, you could use a
    > binary search to locate a DataDescriptor in the table.


    Since he's clearly aiming for maximum speed, the switch block is
    probably the most efficient solution because most compilers will create
    a jump table, which means executing the proper case block will be done
    in O(1) (rather than in O(log n) as with the binary search).

    (Now, of course it's not guaranteed that all compilers will be so smart
    as to generate a jump table from a large switch block, but AFAIK most
    modern compilers do.)

    Of course an alternative solution would be to create the jump table
    explicitly yourself (basically, an array of function pointers which you
    index with the value). This would allow a better rearrangment of the
    code into their own functions, but I don't know if it would result in
    a more, less or equally efficient program. (This solution, however, has
    the disadvantage of making it more complicated for the "case blocks", in
    this case the separate functions, to handle shared local variables. Passing
    them as parameters to the functions might decrease efficiency.)
     
    Juha Nieminen, Aug 10, 2011
    #12
  13. Lynn McGuire

    Jorgen Grahn Guest

    On Wed, 2011-08-10, Juha Nieminen wrote:
    > Victor Bazarov <> wrote:
    >> I wonder, have you tried looking at the generated code? Does the
    >> compiler have a jump table or does it create a bunch of 'if-then goto'
    >> parts? The latter gets expensive for the case labels at the bottom of
    >> the switch statement.

    >
    > One can generally assume that modern compilers are decently good at
    > optimizing large switch blocks. (If a specific compiler isn't, chances
    > are that it isn't good at optimizing anything else either.)


    With such an unusually large switch, I'd rather not assume anything.
    The price for being wrong could be high, like Victor said.

    I'd be interested to hear what the compiler generates in this case.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
     
    Jorgen Grahn, Aug 10, 2011
    #13
  14. Lynn McGuire

    Joe Greer Guest

    Lynn McGuire <> wrote in news:j1rqfi$l6c$:

    > On 8/9/2011 11:27 AM, LR wrote:
    >> Lynn McGuire wrote:
    >>> Is there a maximum number of cases in a switch ?
    >>> I have a class with a method that now has 525
    >>> cases in a switch and will probably double this
    >>> over the next couple of years.
    >>>
    >>> Thanks,
    >>> Lynn

    >>
    >> According to n3242,
    >>
    >> "Case labels for a switch statement (excluding those for any nested
    >> switch statements) [16 384]."

    >
    > A short int, cool ! Gives me plenty of room
    > for additions.
    >
    > Thanks,
    > Lynn
    >
    >


    Have you considered just creating an array of your return values and
    using your symbol as it's index? It would seem to be quicker and more
    straight forward than trying to have a huge switch statement. For
    example:

    static DataDescriptor * myDataDescriptors[] =
    {
    &aDD_GEN_CALVAPPRE,
    &aDD_GEN_CALSONVEL,
    &aDD_GEN_ONLYWRITESTREAM,
    ..
    ..
    ..
    }

    DataDescriptor * GenGroup::descriptor(int aSymbol, int version)
    {
    // do some validating of aSymbol here, can possibly add or subtract
    an offset etc.
    return myDataDescriptors[aSymbol];
    }

    joe
     
    Joe Greer, Aug 10, 2011
    #14
  15. On 8/10/2011 6:40 AM, Juha Nieminen wrote:
    > Marcel Müller<> wrote:
    >> If the DataDescriptors are ordered by the key aSymbol, you could use a
    >> binary search to locate a DataDescriptor in the table.

    >
    > Since he's clearly aiming for maximum speed, the switch block is
    > probably the most efficient solution because most compilers will create
    > a jump table, which means executing the proper case block will be done
    > in O(1) (rather than in O(log n) as with the binary search).
    >
    > (Now, of course it's not guaranteed that all compilers will be so smart
    > as to generate a jump table from a large switch block, but AFAIK most
    > modern compilers do.)
    >
    > Of course an alternative solution would be to create the jump table
    > explicitly yourself (basically, an array of function pointers which you
    > index with the value). This would allow a better rearrangment of the
    > code into their own functions, but I don't know if it would result in
    > a more, less or equally efficient program. (This solution, however, has
    > the disadvantage of making it more complicated for the "case blocks", in
    > this case the separate functions, to handle shared local variables. Passing
    > them as parameters to the functions might decrease efficiency.)


    Not if you *assume* those functions are inlined, like "most modern
    compilers do"...

    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Aug 10, 2011
    #15
  16. On 8/10/2011 8:45 AM, Joe Greer wrote:
    > Lynn McGuire<> wrote in news:j1rqfi$l6c$:
    >
    >> On 8/9/2011 11:27 AM, LR wrote:
    >>> Lynn McGuire wrote:
    >>>> Is there a maximum number of cases in a switch ?
    >>>> I have a class with a method that now has 525
    >>>> cases in a switch and will probably double this
    >>>> over the next couple of years.
    >>>>
    >>>> Thanks,
    >>>> Lynn
    >>>
    >>> According to n3242,
    >>>
    >>> "Case labels for a switch statement (excluding those for any nested
    >>> switch statements) [16 384]."

    >>
    >> A short int, cool ! Gives me plenty of room
    >> for additions.
    >>
    >> Thanks,
    >> Lynn
    >>
    >>

    >
    > Have you considered just creating an array of your return values and
    > using your symbol as it's index? It would seem to be quicker and more
    > straight forward than trying to have a huge switch statement. For
    > example:
    >
    > static DataDescriptor * myDataDescriptors[] =
    > {
    > &aDD_GEN_CALVAPPRE,
    > &aDD_GEN_CALSONVEL,
    > &aDD_GEN_ONLYWRITESTREAM,
    > .
    > .
    > .
    > }

    ;

    And this would only work if the values on which you index the array
    ('GEN_CALVAPPRE', 'GEN_CALSONVEL', etc) are actually ordinal numbers,
    and their order does not change (not that I'm suggesting that it will do
    so often, but still). Otherwise, it's a lookup table, which, granted,
    needs to only be initialized once, and then used all the time, and it
    will only be faster if it's a dense array. As soon as it's a sparse
    array, you either waste memory, or you need to use std::map (or
    'unordered_map') and the time for looking values up is not necessarily
    better...

    > DataDescriptor * GenGroup::descriptor(int aSymbol, int version)
    > {
    > // do some validating of aSymbol here, can possibly add or subtract
    > an offset etc.
    > return myDataDescriptors[aSymbol];
    > }
    >
    > joe


    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Aug 10, 2011
    #16
  17. Lynn McGuire

    Lynn McGuire Guest

    On 8/10/2011 4:10 AM, Marcel Müller wrote:
    > Lynn McGuire wrote:
    >> Incredibly easy. Here is some of the method:
    >>
    >> DataDescriptor aDD_GEN_CALVAPPRE (GEN_CALVAPPRE, "CALVAPPRE",
    >> "Calculate Vapor Pressure for all streams", true, false, false,
    >> SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    >> SYM_DGenInit, 420, nil, nil);
    >>
    >> DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    >> {
    >> switch (aSymbol)
    >> {
    >> case GEN_CALVAPPRE:
    >> return & aDD_GEN_CALVAPPRE;
    >> case GEN_CALSONVEL:
    >> return & aDD_GEN_CALSONVEL;
    >> case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    >> return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    >> ...
    >> }
    >>
    >> // default return
    >> return NULL;
    >> }

    >
    > I would strongly recommend a meta data table.
    >
    > static const DataDescriptor DispatchTable[] =
    > { DataDescriptor(GEN_CALVAPPRE, "CALVAPPRE",
    > "Calculate Vapor Pressure for all streams", true, false, false,
    > SYM_Enumerated, nil, nil, nil, nil, nil, SYM_BOOLEAN,
    > SYM_DGenInit, 420, nil, nil),
    > DataDescriptor(GEN_CALSONVEL, ...),
    > ...
    > };
    >
    > If the DataDescriptors are ordered by the key aSymbol, you could use a binary search to locate a DataDescriptor in the table.
    >
    > Alternatively class DataDescriptor could hold a static repository of type std::map<int,DataDescriptor*> with all it's instances. The
    > constructor of DataDescriptor could register it's own instance in the repository. Then the method GenGroup::descriptor reduces to a
    > hash lookup in the repository. No more than a dozen code lines instead of some thousands in the switch case. And the advantage is
    > that you can keep the code for the DataDescriptor definitions without changes. You only have to delete the large switch.
    >
    >> This is a 470,000 line C++ Win32 user interface for a highly
    >> successful calculation engine.

    >
    > Maybe, but keep in mind that compilers are not well tested with this kind of code. I triggered internal compiler errors with too
    > large functions from time to time (large window procedures).
    >
    >> This code is bulletproof and
    >> very extensible.

    >
    > The two above solutions too.
    >
    >
    > Marcel


    This solution is order dependent. Mine is not. Not that
    I'm looking for a new solution.

    Lynn
     
    Lynn McGuire, Aug 10, 2011
    #17
  18. Lynn McGuire

    Lynn McGuire Guest

    On 8/10/2011 7:20 AM, Jorgen Grahn wrote:
    > On Wed, 2011-08-10, Juha Nieminen wrote:
    >> Victor Bazarov<> wrote:
    >>> I wonder, have you tried looking at the generated code? Does the
    >>> compiler have a jump table or does it create a bunch of 'if-then goto'
    >>> parts? The latter gets expensive for the case labels at the bottom of
    >>> the switch statement.

    >>
    >> One can generally assume that modern compilers are decently good at
    >> optimizing large switch blocks. (If a specific compiler isn't, chances
    >> are that it isn't good at optimizing anything else either.)

    >
    > With such an unusually large switch, I'd rather not assume anything.
    > The price for being wrong could be high, like Victor said.
    >
    > I'd be interested to hear what the compiler generates in this case.
    >
    > /Jorgen


    DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    {
    0075D5B0 push ebp
    0075D5B1 mov ebp,esp
    0075D5B3 sub esp,8
    0075D5B6 mov dword ptr [ebp-4],ecx
    switch (aSymbol)
    0075D5B9 mov eax,dword ptr [aSymbol]
    0075D5BC mov dword ptr [ebp-8],eax
    0075D5BF cmp dword ptr [ebp-8],20Ch
    0075D5C6 ja $LN1+7 (75EA22h)
    0075D5CC mov ecx,dword ptr [ebp-8]
    0075D5CF jmp dword ptr (75EA2Ch)[ecx*4]
    {
    case GEN_CALVAPPRE:
    return & aDD_GEN_CALVAPPRE;
    0075D5D6 mov eax,offset aDD_GEN_CALVAPPRE (0DA0080h)
    0075D5DB jmp $LN1+9 (75EA24h)
    case GEN_CALSONVEL:
    return & aDD_GEN_CALSONVEL;
    0075D5E0 mov eax,offset aDD_GEN_CALSONVEL (0D88908h)
    0075D5E5 jmp $LN1+9 (75EA24h)
    case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    0075D5EA mov eax,offset aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET (0D9C648h)
    0075D5EF jmp $LN1+9 (75EA24h)
    ....

    Lynn
     
    Lynn McGuire, Aug 10, 2011
    #18
  19. Lynn McGuire wrote:

    > On 8/10/2011 7:20 AM, Jorgen Grahn wrote:
    >> On Wed, 2011-08-10, Juha Nieminen wrote:
    >>> Victor Bazarov<> wrote:
    >>>> I wonder, have you tried looking at the generated code? Does the
    >>>> compiler have a jump table or does it create a bunch of 'if-then goto'
    >>>> parts? The latter gets expensive for the case labels at the bottom of
    >>>> the switch statement.
    >>>
    >>> One can generally assume that modern compilers are decently good at
    >>> optimizing large switch blocks. (If a specific compiler isn't, chances
    >>> are that it isn't good at optimizing anything else either.)

    >>
    >> With such an unusually large switch, I'd rather not assume anything.
    >> The price for being wrong could be high, like Victor said.
    >>
    >> I'd be interested to hear what the compiler generates in this case.
    >>
    >> /Jorgen

    >
    > DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    > {
    > 0075D5B0 push ebp
    > 0075D5B1 mov ebp,esp
    > 0075D5B3 sub esp,8
    > 0075D5B6 mov dword ptr [ebp-4],ecx
    > switch (aSymbol)
    > 0075D5B9 mov eax,dword ptr [aSymbol]
    > 0075D5BC mov dword ptr [ebp-8],eax
    > 0075D5BF cmp dword ptr [ebp-8],20Ch
    > 0075D5C6 ja $LN1+7 (75EA22h)
    > 0075D5CC mov ecx,dword ptr [ebp-8]
    > 0075D5CF jmp dword ptr (75EA2Ch)[ecx*4]
    > {
    > case GEN_CALVAPPRE:
    > return & aDD_GEN_CALVAPPRE;
    > 0075D5D6 mov eax,offset aDD_GEN_CALVAPPRE (0DA0080h)
    > 0075D5DB jmp $LN1+9 (75EA24h)
    > case GEN_CALSONVEL:
    > return & aDD_GEN_CALSONVEL;
    > 0075D5E0 mov eax,offset aDD_GEN_CALSONVEL (0D88908h)
    > 0075D5E5 jmp $LN1+9 (75EA24h)
    > case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    > return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    > 0075D5EA mov eax,offset aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET
    > (0D9C648h)
    > 0075D5EF jmp $LN1+9 (75EA24h)


    Silly compiler. It should store the return value directly in the table and
    avoid the unnecessary jump. ;)
     
    Paul Brettschneider, Aug 10, 2011
    #19
  20. Paul Brettschneider wrote:

    > Lynn McGuire wrote:
    >
    >> On 8/10/2011 7:20 AM, Jorgen Grahn wrote:
    >>> On Wed, 2011-08-10, Juha Nieminen wrote:
    >>>> Victor Bazarov<> wrote:
    >>>>> I wonder, have you tried looking at the generated code? Does the
    >>>>> compiler have a jump table or does it create a bunch of 'if-then goto'
    >>>>> parts? The latter gets expensive for the case labels at the bottom of
    >>>>> the switch statement.
    >>>>
    >>>> One can generally assume that modern compilers are decently good at
    >>>> optimizing large switch blocks. (If a specific compiler isn't, chances
    >>>> are that it isn't good at optimizing anything else either.)
    >>>
    >>> With such an unusually large switch, I'd rather not assume anything.
    >>> The price for being wrong could be high, like Victor said.
    >>>
    >>> I'd be interested to hear what the compiler generates in this case.
    >>>
    >>> /Jorgen

    >>
    >> DataDescriptor * GenGroup::descriptor (int aSymbol, int version)
    >> {
    >> 0075D5B0 push ebp
    >> 0075D5B1 mov ebp,esp
    >> 0075D5B3 sub esp,8
    >> 0075D5B6 mov dword ptr [ebp-4],ecx
    >> switch (aSymbol)
    >> 0075D5B9 mov eax,dword ptr [aSymbol]
    >> 0075D5BC mov dword ptr [ebp-8],eax
    >> 0075D5BF cmp dword ptr [ebp-8],20Ch
    >> 0075D5C6 ja $LN1+7 (75EA22h)
    >> 0075D5CC mov ecx,dword ptr [ebp-8]
    >> 0075D5CF jmp dword ptr (75EA2Ch)[ecx*4]
    >> {
    >> case GEN_CALVAPPRE:
    >> return & aDD_GEN_CALVAPPRE;
    >> 0075D5D6 mov eax,offset aDD_GEN_CALVAPPRE (0DA0080h)
    >> 0075D5DB jmp $LN1+9 (75EA24h)
    >> case GEN_CALSONVEL:
    >> return & aDD_GEN_CALSONVEL;
    >> 0075D5E0 mov eax,offset aDD_GEN_CALSONVEL (0D88908h)
    >> 0075D5E5 jmp $LN1+9 (75EA24h)
    >> case GEN_ONLYWRITESTREAMBOXONFIRSTSHEET:
    >> return & aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET;
    >> 0075D5EA mov eax,offset aDD_GEN_ONLYWRITESTREAMBOXONFIRSTSHEET
    >> (0D9C648h)
    >> 0075D5EF jmp $LN1+9 (75EA24h)

    >
    > Silly compiler. It should store the return value directly in the table and
    > avoid the unnecessary jump. ;)


    Actually g++ does just that, unless you have a special case/default label
    which for example throws an exception. Weird.

    Test code:
    #include <iostream>
    int main()
    {
    std::cout << "#include <stdexcept>\n"
    << "class X { int i; };\n";
    for(int i = 0; i < 100; ++i) {
    std::cout << "X x" << i << ";\n";
    }
    std::cout << "X &blah(int x)\n"
    << "{\n"
    << "\tswitch(x) {\n";
    for(int i = 0; i < 100; ++i) {
    std::cout << "\tcase " << i << ":"
    << " return x" << i << ";\n";
    }
    // Comment in default-label for code pessimisation
    std::cout << "\t//default: throw std::runtime_error(\"Oh no!\");\n"
    << "\t}\n"
    << "\treturn x0;\n"
    << "}\n";
    }

    resulting "jump" table:
    CSWTCH.1:
    .long x0
    .long x1
    .long x2
    .long x3
    .long x4
    .long x5
    ...
     
    Paul Brettschneider, Aug 10, 2011
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Bryan Parkoff

    65536 Switch Cases

    Bryan Parkoff, Sep 16, 2004, in forum: C++
    Replies:
    5
    Views:
    458
    Ivan Vecerina
    Sep 17, 2004
  2. z-man
    Replies:
    7
    Views:
    395
    Tor Iver Wilhelmsen
    Oct 5, 2006
  3. He Shiming

    Hundreds of cases in a switch, optimization?

    He Shiming, Jun 19, 2005, in forum: C Programming
    Replies:
    65
    Views:
    1,706
    Christian Bau
    Jun 24, 2005
  4. Ronny
    Replies:
    6
    Views:
    133
  5. phanhuyich
    Replies:
    4
    Views:
    278
Loading...

Share This Page