-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DO NOT MERGE: less ref counting #1
base: master
Are you sure you want to change the base?
Conversation
func on_headers_complete() -> Int | ||
func on_body(at: UnsafePointer<UInt8>, length: Int) -> Int | ||
func on_message_complete() -> Int | ||
public struct http_parser_delegate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using escaping closures instead of a protocol was way slower in my tests.
Unrelated, missing in the original too: the callbacks need the parser as the first argument (like in the original) to provide the necessary context (w/o requiring capturing closures).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
struct http_parser_delegate
combined with struct http_parser
is indeed faster. I tried protocol http_parser_delegate
with struct http_parser
and it was slower than using protocol http_parser_delegate
and class http_parser
due to extra thunking inserted by the compiler.
@helje5, as you point out, the bench.c program does nothing, so it does not measure any penalty referencing a required context. What we should consider is creating a benchmark where the 'C' and 'Swift' version both use the context to perform something more meaningful (like count callbacks and byte counts). We should see the benchmark results get closer.
Another approach would be wrap the 'C' version with a Swift class like Kitura's HTTPParser wrapper and measure the performance with a pure Swift implementation. This would be closer to where we need to eventually be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would really like to know why a protocol would be any slower. What is that 'thunking', is that documented somewhere? I would expect a (non objc Swift) protocol to be essentially a plain vtable. That should be way faster than escaping closures.
I don't understand the HTTPParser wrapper you are pointing to. That looks entirely pointless/superfluous to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let on_chunk_header: () -> Int | ||
let on_chunk_complete: () -> Int | ||
|
||
public init(on_message_begin: @escaping () -> Int, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the original the parser itself doesn't maintain the callbacks, it is kept in a separate struct which is passed to execute(). This makes sense because the parser is reentrant/stateless and the mapping is usually static (only one copy needed). Remember that the idea is to keep the per-connection overhead as small as possible (in the original only 40 bytes per connection!).
@@ -176,7 +312,7 @@ private let F_SKIPBODY: http_flags = 1 << 6 | |||
private let F_CONTENTLENGTH: http_flags = 1 << 7 | |||
|
|||
/* Map for errno-related constants */ | |||
public enum http_errno: String, Error { | |||
public enum http_errno: StaticString, Error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be a String in the first place but just an Int num. You never use those cases as Strings anyways but the description property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following function uses the String (StaticString) to provide the name of the error via .rawValue:
/* Return a string name of the given error */
public class func errno_name(_ err: http_errno) -> String {
return err.rawValue
}
This function is not used in bench.c/.swift so I thought it would not effect performance. However I replaced StaticString with String and the benchmark did slow down. More investigation is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I was kinda wrong on this one. I was assuming that a Swift String enum
would use Strings at runtime to match cases. That actually doesn't seem to be the case. The enum is always some small integer and it the compiler generates the switch to do the raw value mapping from/to string.
But for the specific function you mention, that kinda works for any enum: "(err)" and matches what the C variant does.
@@ -254,17 +390,18 @@ public enum http_errno: String, Error { | |||
} | |||
} | |||
|
|||
public class http_parser { | |||
private func LIKELY(_ X: Bool) -> Bool { return X } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would @inline(__always)
such until the compiler crashes :-)
(__)
/ .\/. ______
| /\_| | \
| |___ | |
| ---@ |_______|
* | | ---- | |
\ | |_____
\|________|
CompuCow Discovers Bug in Compiler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this on a number of functions like LOWER(), IS_ALPHA(), IS_NUM(), IS_ALPHANUM()
and I did not see any performance improvement in Release mode.
@@ -845,15 +861,15 @@ private func IS_HEADER_CHAR(_ ch: UInt8) -> Bool { | |||
return (ch == CR || ch == LF || ch == 9 || (ch > 31 && ch != 127)) | |||
} | |||
|
|||
private func STRICT_TOKEN(_ c: UInt8) -> UInt8 { | |||
private mutating func STRICT_TOKEN(_ c: UInt8) -> UInt8 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't mutating?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mutating is required on 10 or so functions if using struct http_parser
. This is not required using class http_parser
.
The compiler error is:
Mark method 'mutating' to make 'self' mutable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you are trying to say here. This specific function (STRICT_TOKEN
) isn't mutating. It is just: return tokens[Int(c)]
. If the compiler throws an error on this, it really looks like a bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct. STRICT_TOKEN does not require mutating
. My mistake.
this is a mess, sorry
but
before:
after: