Forums

Naming convention for constants

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

Naming convention for constants

Postby nerdzero » Mon Sep 30, 2013 11:56 am

I'm writing a lot of String constants for a new Cobra parser in support of better autocompletion in the MonoDevelop addin when syntax errors exist in the source code. I have a style question.

In .NET, it is recommended to use PascalCase.

Since Python doesn't have constants, it is recommended to use ALL_CAPS to indicate intent.

In the Cobra compiler source code, camelCase is used for constants.

However, in all those languages, any convention is allowed. So what do you guys use? I've been using camelCase but now I'm thinking I should be using ALL_CAPS (even if it is uglier) as the intent is more clear when just looking at the code in an editor or in a browser that doesn't have support for tooltips. Opinions?
nerdzero
 
Posts: 286
Location: Chicago, IL

Re: Naming convention for constants

Postby Charles » Mon Sep 30, 2013 5:19 pm

The reason I use .fooBar for constants is that I don't want to care very much that they are constants as opposed to vars, properties or methods. If down the road, I change .fooBar from a constant to a property backed by a config file, it will still be called .fooBar. If I never change it to be backed by a config file, it's still perfectly readable as .fooBar.

I kind of regret not going that route with enums members.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Naming convention for constants

Postby nerdzero » Mon Sep 30, 2013 7:22 pm

Ah, that's interesting and makes sense in that case. I see now why no strict naming convention is enforced. In this case though, I am hoping to leverage the fact that string constants are interned so I can just do a nice and fast reference equality check when comparing. So, I wouldn't plan on ever changing them from constants otherwise it would break the equality checks.
nerdzero
 
Posts: 286
Location: Chicago, IL

Re: Naming convention for constants

Postby Charles » Mon Sep 30, 2013 9:38 pm

If you could do any benchmarks between the interned string comparisons vs. enums vs. int constants, that could be interesting. Or maybe such things are already out there for C# though we'd have to pay attention to what version of .NET or Mono they ran.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Naming convention for constants

Postby nerdzero » Tue Oct 01, 2013 9:50 am

Heh, okay so I did a quick benchmark and interned string comparison is faster but it doesn't matter and I'll explain why. Here's the code I ended up using. One method uses the "==" operator and the other uses the "is" operator.

class Program

const NAMESPACE = "NAMESPACE"
const CLASS = "CLASS"
const DEF = "DEF"
const PRINT = "PRINT"
const ID = "ID"
const DOT = "DOT"
const STRING_SINGLE = "STRING_SINGLE"
const INDENT = "INDENT"
const DEDENT = "DEDENT"
const EOL = "EOL"
const EOF = "EOF"

var iterations = 100_000

def main

tokenKinds = [
'NAMESPACE', 'ID', 'DOT', 'ID', 'EOL', 'EOL', 'INDENT', 'CLASS', 'ID', 'EOL',
'EOL', 'INDENT', 'DEF', 'ID', 'EOL', 'EOL', 'INDENT', 'PRINT', 'STRING_SINGLE',
'EOL', 'DEDENT', 'DEDENT', 'DEDENT', 'EOF'
]

.benchmarkEqualsOperator(tokenKinds)
.benchmarkIsOperator(tokenKinds)

def benchmarkEqualsOperator(tokenKinds as List<of String>)
sw = Diagnostics.Stopwatch()
sw.start
for i in .iterations
for kind in tokenKinds
if kind == .NAMESPACE
pass
else if kind == .CLASS
pass
else if kind == .DEF
pass
else if kind == .PRINT
pass
else if kind == .ID
pass
else if kind == .DOT
pass
else if kind == .STRING_SINGLE
pass
else if kind == .INDENT
pass
else if kind == .DEDENT
pass
else if kind == .EOL
pass
else if kind == .EOF
pass
else
throw FallThroughException("No clause for '[kind]'")
sw.stop
print "Using '==' operator: [sw.elapsedMilliseconds] ms"

def benchmarkIsOperator(tokenKinds as List<of String>)
sw = Diagnostics.Stopwatch()
sw.start
for i in .iterations
for kind in tokenKinds

if kind is .NAMESPACE
pass
else if kind is .CLASS
pass
else if kind is .DEF
pass
else if kind is .PRINT
pass
else if kind is .ID
pass
else if kind is .DOT
pass
else if kind is .STRING_SINGLE
pass
else if kind is .INDENT
pass
else if kind is .DEDENT
pass
else if kind is .EOL
pass
else if kind is .EOF
pass
else
throw FallThroughException("No clause for '[kind]' or it is not interned")
sw.stop
print "Using 'is' operator: [sw.elapsedMilliseconds] ms"


Here's some results on a decently beefy .NET machine

Code: Select all
Using '==' operator: 72 ms
Using 'is' operator: 29 ms

However, like most benchmarks, these results are artificial. When I actually try to use this approach using a token list obtained from a CobraTokenizer instance I run into an issue. Not all the strings are interned in that case and so the process of determining if a string is interned (and then retrieving the interned version if not) makes the equals operator approach faster. A LOT faster in fact. Adding this code before doing any 'is' comparisons...

if not String.isInterned(kind)
kind = String.intern(kind) to !


...made the time shoot up from 29 ms to 414 ms. I even tried getting creative with a local dictionary mapping each string constant to itself, but the best I could do was bring it down to 119 ms. Still slower than ==. I also tried a third approach using kind.equals(.SOME_CONST, StringComparison.Ordinal) for grins which clocked in around 110 ms.

So, == it is! I always know I shouldn't do any premature optimizations but I just can't help myself sometimes :) Too clever for my own good!
nerdzero
 
Posts: 286
Location: Chicago, IL

Re: Naming convention for constants

Postby Charles » Tue Oct 01, 2013 11:29 am

Are enums or ints an option?
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Naming convention for constants

Postby nerdzero » Tue Oct 01, 2013 12:32 pm

In what way? Dictionary that maps token strings to id values?
nerdzero
 
Posts: 286
Location: Chicago, IL

Re: Naming convention for constants

Postby Charles » Tue Oct 01, 2013 5:52 pm

No I was thinking Token.which could be an int or enum instead of a string. This is actually the more common approach if you look at various lexers in various languages.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Naming convention for constants

Postby nerdzero » Wed Oct 02, 2013 9:13 am

Oh, I see. I wasn't planning on modifying the tokenizer, but yeah both NRefactory and Roslyn use enums.

I added this code to the benchmark:

enum TokenKind
Namespace
Class
Def
Print
Id
Dot
StringSingle
Indent
Dedent
Eol
Eof

class Program

...

def main

...

tokenKinds2 as List<of TokenKind> = [
TokenKind.Namespace, TokenKind.Id, TokenKind.Dot, TokenKind.Id, TokenKind.Eol,
TokenKind.Eol, TokenKind.Indent, TokenKind.Class, TokenKind.Id, TokenKind.Eol,
TokenKind.Eol, TokenKind.Indent, TokenKind.Def, TokenKind.Id, TokenKind.Eol,
TokenKind.Eol, TokenKind.Indent, TokenKind.Print, TokenKind.StringSingle,
TokenKind.Eol, TokenKind.Dedent, TokenKind.Dedent, TokenKind.Dedent,
TokenKind.Eof
]

.benchmarkEnum(tokenKinds2)

...

def benchmarkEnum(tokenKinds as List<of TokenKind>)
sw = Diagnostics.Stopwatch()
sw.start
for i in .iterations
for kind in tokenKinds

if kind == TokenKind.Namespace
pass
else if kind == TokenKind.Class
pass
else if kind == TokenKind.Def
pass
else if kind == TokenKind.Print
pass
else if kind == TokenKind.Id
pass
else if kind == TokenKind.Dot
pass
else if kind == TokenKind.StringSingle
pass
else if kind == TokenKind.Indent
pass
else if kind == TokenKind.Dedent
pass
else if kind == TokenKind.Eol
pass
else if kind == TokenKind.Eof
pass
else
throw FallThroughException("No clause for '[kind]'")
sw.stop
print "Using 'enum' comparison: [sw.elapsedMilliseconds] ms"


Here's results I can't explain though:

Code: Select all
Using '==' operator: 80 ms
Using 'is' operator: 26 ms
Using 'enum' comparison: 241 ms

Why's it so slow? Did I do something inefficiently? Is boxing and unboxing adding a significant overhead here?
nerdzero
 
Posts: 286
Location: Chicago, IL

Re: Naming convention for constants

Postby Charles » Wed Oct 02, 2013 11:20 pm

I saw a post on enums being slow under some circumstances in .NET, but don't recall the details.

Two things you can try quickly though:

1) Run with -turbo and see if there is any improvement

2) Change "kind == TokenKind.Namespace" to "kind is TokenKind.Namespace"

3) Change the if-else ladder to a "branch"

Note that regarding 3) some C compilers can do some fancy optimizations for switch() statements involving JMPing to the code location for the case. I don't know if .NET's JIT will do that or not.


Also, without investigating further, I wonder if Cobra is generating a "CobraImp.Equals()" call for your enum comparisons instead of "==". If so, we can fix this. You can check with -kif. See cobra -help.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Next

Return to Discussion

Who is online

Users browsing this forum: No registered users and 31 guests