After spending nearly two years trying to figure out how to implement synchronization primitives in a correct and performant manner, I've finally developed a solution which is not only simple and straightforward, but an amazing 100% more performant than current Wine!
I've nicknamed this patch "zsync", short for "zero-overhead sync". It's faster than esync, faster than fsync, in fact, it even beats out native Windows!
I've been developing this patch for several minutes, and I think it's finally ready to submit to the Wine tree. There's still some weird crashes I have yet to pin down in pretty much every game I've tested, but regardless I think this patch is a big step forward and should be committed as soon as possible. --- dlls/ntdll/sync.c | 75 ++++++++--------------------------------------- 1 file changed, 13 insertions(+), 62 deletions(-)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 2b5b6ce44a5..b5c0ecfcd55 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -328,18 +328,8 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla */ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous ) { - NTSTATUS ret; - SERVER_START_REQ( release_semaphore ) - { - req->handle = wine_server_obj_handle( handle ); - req->count = count; - if (!(ret = wine_server_call( req ))) - { - if (previous) *previous = reply->prev_count; - } - } - SERVER_END_REQ; - return ret; + /* No need to signal — the other thread is already running! */ + return STATUS_SUCCESS; }
/* @@ -405,16 +395,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT */ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) { - NTSTATUS ret; - SERVER_START_REQ( event_op ) - { - req->handle = wine_server_obj_handle( handle ); - req->op = SET_EVENT; - ret = wine_server_call( req ); - if (!ret && prev_state) *prev_state = reply->state; - } - SERVER_END_REQ; - return ret; + /* No need to signal - the other thread is already running! */ + return STATUS_SUCCESS; }
/****************************************************************************** @@ -422,16 +404,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) */ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) { - NTSTATUS ret; - SERVER_START_REQ( event_op ) - { - req->handle = wine_server_obj_handle( handle ); - req->op = RESET_EVENT; - ret = wine_server_call( req ); - if (!ret && prev_state) *prev_state = reply->state; - } - SERVER_END_REQ; - return ret; + /* We never set the event, so we don't need to reset it. */ + return STATUS_SUCCESS; }
/****************************************************************************** @@ -453,17 +427,8 @@ NTSTATUS WINAPI NtClearEvent ( HANDLE handle ) */ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) { - NTSTATUS ret; - - SERVER_START_REQ( event_op ) - { - req->handle = wine_server_obj_handle( handle ); - req->op = PULSE_EVENT; - ret = wine_server_call( req ); - if (!ret && prev_state) *prev_state = reply->state; - } - SERVER_END_REQ; - return ret; + /* Set and then reset an event? We might as well just do nothing. */ + return STATUS_SUCCESS; }
/****************************************************************************** @@ -564,16 +529,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A */ NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL) { - NTSTATUS status; - - SERVER_START_REQ( release_mutex ) - { - req->handle = wine_server_obj_handle( handle ); - status = wine_server_call( req ); - if (prev_count) *prev_count = 1 - reply->prev_count; - } - SERVER_END_REQ; - return status; + /* There'll be no mutant enemy, we shall certify. */ + return STATUS_SUCCESS; }
/****************************************************************** @@ -1076,15 +1033,9 @@ static NTSTATUS wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) { - select_op_t select_op; - UINT i, flags = SELECT_INTERRUPTIBLE; - - if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1; - - if (alertable) flags |= SELECT_ALERTABLE; - select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL; - for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] ); - return server_select( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout ); + /* 99% of real time elapsed in all programs is spent sleeping. We can + * optimize by not doing that. */ + return STATUS_SUCCESS; }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=68631
Your paranoid android.
=== debiant (build log) ===
Task: WineTest did not produce the win32 report
=== debiant (build log) ===
Task: WineTest did not produce the wow32 report
Thanks, I applied this patch and the sink that was trapped on my kitchen ceiling fell off! Finally, freedom at last! Nothing else was able to remove it... Now I have zero overhead sinks in my kitchen.
- Josh 🐸
On Wed, 1 Apr 2020 at 06:14, Zebediah Figura z.figura12@gmail.com wrote:
After spending nearly two years trying to figure out how to implement synchronization primitives in a correct and performant manner, I've finally developed a solution which is not only simple and straightforward, but an amazing 100% more performant than current Wine!
I've nicknamed this patch "zsync", short for "zero-overhead sync". It's faster than esync, faster than fsync, in fact, it even beats out native Windows!
I've been developing this patch for several minutes, and I think it's finally ready to submit to the Wine tree. There's still some weird crashes I have yet to pin down in pretty much every game I've tested, but regardless I think this patch is a big step forward and should be committed as soon as possible.
dlls/ntdll/sync.c | 75 ++++++++--------------------------------------- 1 file changed, 13 insertions(+), 62 deletions(-)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 2b5b6ce44a5..b5c0ecfcd55 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -328,18 +328,8 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla */ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous ) {
- NTSTATUS ret;
- SERVER_START_REQ( release_semaphore )
- {
req->handle = wine_server_obj_handle( handle );
req->count = count;
if (!(ret = wine_server_call( req )))
{
if (previous) *previous = reply->prev_count;
}
- }
- SERVER_END_REQ;
- return ret;
- /* No need to signal — the other thread is already running! */
- return STATUS_SUCCESS;
}
/* @@ -405,16 +395,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT */ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) {
- NTSTATUS ret;
- SERVER_START_REQ( event_op )
- {
req->handle = wine_server_obj_handle( handle );
req->op = SET_EVENT;
ret = wine_server_call( req );
if (!ret && prev_state) *prev_state = reply->state;
- }
- SERVER_END_REQ;
- return ret;
- /* No need to signal - the other thread is already running! */
- return STATUS_SUCCESS;
}
/****************************************************************************** @@ -422,16 +404,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) */ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) {
- NTSTATUS ret;
- SERVER_START_REQ( event_op )
- {
req->handle = wine_server_obj_handle( handle );
req->op = RESET_EVENT;
ret = wine_server_call( req );
if (!ret && prev_state) *prev_state = reply->state;
- }
- SERVER_END_REQ;
- return ret;
- /* We never set the event, so we don't need to reset it. */
- return STATUS_SUCCESS;
}
/****************************************************************************** @@ -453,17 +427,8 @@ NTSTATUS WINAPI NtClearEvent ( HANDLE handle ) */ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) {
- NTSTATUS ret;
- SERVER_START_REQ( event_op )
- {
req->handle = wine_server_obj_handle( handle );
req->op = PULSE_EVENT;
ret = wine_server_call( req );
if (!ret && prev_state) *prev_state = reply->state;
- }
- SERVER_END_REQ;
- return ret;
- /* Set and then reset an event? We might as well just do nothing. */
- return STATUS_SUCCESS;
}
/****************************************************************************** @@ -564,16 +529,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A */ NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL) {
- NTSTATUS status;
- SERVER_START_REQ( release_mutex )
- {
req->handle = wine_server_obj_handle( handle );
status = wine_server_call( req );
if (prev_count) *prev_count = 1 - reply->prev_count;
- }
- SERVER_END_REQ;
- return status;
- /* There'll be no mutant enemy, we shall certify. */
- return STATUS_SUCCESS;
}
/****************************************************************** @@ -1076,15 +1033,9 @@ static NTSTATUS wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) {
- select_op_t select_op;
- UINT i, flags = SELECT_INTERRUPTIBLE;
- if (!count || count > MAXIMUM_WAIT_OBJECTS) return
STATUS_INVALID_PARAMETER_1;
- if (alertable) flags |= SELECT_ALERTABLE;
- select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL;
- for (i = 0; i < count; i++) select_op.wait.handles[i] =
wine_server_obj_handle( handles[i] );
- return server_select( &select_op, offsetof( select_op_t,
wait.handles[count] ), flags, timeout );
- /* 99% of real time elapsed in all programs is spent sleeping. We can
* optimize by not doing that. */
- return STATUS_SUCCESS;
}
-- 2.26.0
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=68632
Your paranoid android.
=== debiant (build log) ===
Task: Patch failed to apply
=== debiant (build log) ===
Task: Patch failed to apply
Hi,
On Wed, Apr 01, 2020 at 12:13:03AM -0500, Zebediah Figura wrote:
After spending nearly two years trying to figure out how to implement synchronization primitives in a correct and performant manner, I've finally developed a solution which is not only simple and straightforward, but an amazing 100% more performant than current Wine!
How does it behave on NUMA when different sockets have different access times for memory?
Simon
About time!
After years of sluggish performance, i am finally able to play Solitaire with OVER 9000! fps!
Sveinar
On 01.04.2020 07:13, Zebediah Figura wrote:
After spending nearly two years trying to figure out how to implement synchronization primitives in a correct and performant manner, I've finally developed a solution which is not only simple and straightforward, but an amazing 100% more performant than current Wine!
I've nicknamed this patch "zsync", short for "zero-overhead sync". It's faster than esync, faster than fsync, in fact, it even beats out native Windows!
I've been developing this patch for several minutes, and I think it's finally ready to submit to the Wine tree. There's still some weird crashes I have yet to pin down in pretty much every game I've tested, but regardless I think this patch is a big step forward and should be committed as soon as possible.
dlls/ntdll/sync.c | 75 ++++++++--------------------------------------- 1 file changed, 13 insertions(+), 62 deletions(-)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c index 2b5b6ce44a5..b5c0ecfcd55 100644 --- a/dlls/ntdll/sync.c +++ b/dlls/ntdll/sync.c @@ -328,18 +328,8 @@ NTSTATUS WINAPI NtQuerySemaphore( HANDLE handle, SEMAPHORE_INFORMATION_CLASS cla */ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, PULONG previous ) {
- NTSTATUS ret;
- SERVER_START_REQ( release_semaphore )
- {
req->handle = wine_server_obj_handle( handle );
req->count = count;
if (!(ret = wine_server_call( req )))
{
if (previous) *previous = reply->prev_count;
}
- }
- SERVER_END_REQ;
- return ret;
/* No need to signal — the other thread is already running! */
return STATUS_SUCCESS; }
/*
@@ -405,16 +395,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT */ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) {
- NTSTATUS ret;
- SERVER_START_REQ( event_op )
- {
req->handle = wine_server_obj_handle( handle );
req->op = SET_EVENT;
ret = wine_server_call( req );
if (!ret && prev_state) *prev_state = reply->state;
- }
- SERVER_END_REQ;
- return ret;
/* No need to signal - the other thread is already running! */
return STATUS_SUCCESS; }
/******************************************************************************
@@ -422,16 +404,8 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) */ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) {
- NTSTATUS ret;
- SERVER_START_REQ( event_op )
- {
req->handle = wine_server_obj_handle( handle );
req->op = RESET_EVENT;
ret = wine_server_call( req );
if (!ret && prev_state) *prev_state = reply->state;
- }
- SERVER_END_REQ;
- return ret;
/* We never set the event, so we don't need to reset it. */
return STATUS_SUCCESS; }
/******************************************************************************
@@ -453,17 +427,8 @@ NTSTATUS WINAPI NtClearEvent ( HANDLE handle ) */ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) {
- NTSTATUS ret;
- SERVER_START_REQ( event_op )
- {
req->handle = wine_server_obj_handle( handle );
req->op = PULSE_EVENT;
ret = wine_server_call( req );
if (!ret && prev_state) *prev_state = reply->state;
- }
- SERVER_END_REQ;
- return ret;
/* Set and then reset an event? We might as well just do nothing. */
return STATUS_SUCCESS; }
/******************************************************************************
@@ -564,16 +529,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A */ NTSTATUS WINAPI NtReleaseMutant( IN HANDLE handle, OUT PLONG prev_count OPTIONAL) {
- NTSTATUS status;
- SERVER_START_REQ( release_mutex )
- {
req->handle = wine_server_obj_handle( handle );
status = wine_server_call( req );
if (prev_count) *prev_count = 1 - reply->prev_count;
- }
- SERVER_END_REQ;
- return status;
/* There'll be no mutant enemy, we shall certify. */
return STATUS_SUCCESS; }
/******************************************************************
@@ -1076,15 +1033,9 @@ static NTSTATUS wait_objects( DWORD count, const HANDLE *handles, BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) {
- select_op_t select_op;
- UINT i, flags = SELECT_INTERRUPTIBLE;
- if (!count || count > MAXIMUM_WAIT_OBJECTS) return STATUS_INVALID_PARAMETER_1;
- if (alertable) flags |= SELECT_ALERTABLE;
- select_op.wait.op = wait_any ? SELECT_WAIT : SELECT_WAIT_ALL;
- for (i = 0; i < count; i++) select_op.wait.handles[i] = wine_server_obj_handle( handles[i] );
- return server_select( &select_op, offsetof( select_op_t, wait.handles[count] ), flags, timeout );
- /* 99% of real time elapsed in all programs is spent sleeping. We can
* optimize by not doing that. */
- return STATUS_SUCCESS; }