wishel 发表于 2009-11-3 15:14:10

解决ACE_WIN32_Asynch_Write_File::write的问题

ACE_WIN32_Asynch_Write_File继承的ACE_WIN32_Asynch_Write_Stream,它调用write时就调用了后者的shared_write,在有Winsock2的系统中调用的是WSASend() api。当写socket时没关系,但是当写文件时就不被接受了。先看以下两段文字:

1,ACE注释
// Initiate the write; Winsock 2 is required for the higher-performing
// WSASend() function. For Winsock 1, fall back to the slower WriteFile().
2,摘自《windows网络编程 2ed》:
With the release of Winsock 2, overlapped I/O can still be used with the functions ReadFile and WriteFile under Windows NT and Windows 2000. However, this functionality was not added to Windows 95, Windows 98, and Windows Me. For compatibility across platforms, you should always consider using the WSARecv and WSASend functions instead of the Windows ReadFile and WriteFile functions.

ACE的注释说WSASend()要比WriteFile()性能好,不知有何根据。根据我个人理解,都是overlapped io,虽然接口不同,但底层调用的应该是相同的东西。因此他们的区别仅在于兼容性。
《windows网络编程》建议统一用WSASend,是出于兼容性考虑(For compatibility across platforms)。但是ACE的设计是ACE_WIN32_Asynch_Write_File继承了ACE_WIN32_Asynch_Write_Stream,也继承了后者的write()。那这个write就只能用WriteFile()。
或者对设计进行改动,让ACE_WIN32_Asynch_Write_File::write() override基类的write()。似乎这不是个好主意,代码能重用还是尽量重用的好。

而ACE_WIN32_Asynch_Read_Stream::read()就很好,只用ReadFile()而没有用WSARecv()。也不说WSARecv()比ReadFile()性能好了。

wishel 发表于 2009-11-3 15:19:10

修改代码WIN32_Asynch_IO.cpp中ACE_WIN32_Asynch_Write_Stream::shared_write

原:

#if (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0))
   WSABUF iov;
   iov.buf = result->message_block ().rd_ptr ();
   iov.len = bytes_to_write;
   initiate_result = ::WSASend (reinterpret_cast<SOCKET> (result->handle ()),
                              &iov,
                              1,
                              &bytes_written,
                              0, // flags
                              result,
                              0);
   if (initiate_result == 0)
   // Immediate success: the OVERLAPPED will still get queued.
   return 0;
#else
initiate_result = ::WriteFile (result->handle (),
                                 result->message_block ().rd_ptr (),
                                 bytes_to_write,
                                 &bytes_written,
                                 result);
if (initiate_result == 1)
    // Immediate success: the OVERLAPPED will still get queued.
    return 0;
#endif /* ACE_HAS_WINSOCK2 */


改为

// #if (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0))
//   WSABUF iov;
//   iov.buf = result->message_block ().rd_ptr ();
//   iov.len = bytes_to_write;
//   initiate_result = ::WSASend (reinterpret_cast<SOCKET> (result->handle ()),
//                              &iov,
//                              1,
//                              &bytes_written,
//                              0, // flags
//                              result,
//                              0);
//   if (initiate_result == 0)
//   // Immediate success: the OVERLAPPED will still get queued.
//   return 0;
// #else
initiate_result = ::WriteFile (result->handle (),
                                 result->message_block ().rd_ptr (),
                                 bytes_to_write,
                                 &bytes_written,
                                 result);
if (initiate_result == 1)
    // Immediate success: the OVERLAPPED will still get queued.
    return 0;
//#endif /* ACE_HAS_WINSOCK2 */


再重新编译ACE即可。

rotar 发表于 2009-11-3 16:50:37

wishel 我按你说的改了(其实昨天我也是这么改的)
改完写文件写不全,只写一点。

modern 发表于 2009-11-4 13:32:43

参考http://support.microsoft.com/kb/192800/en-us/,
微软自己也说可能会产生更多的线程上下文切换与参数的整编,似乎ACE的设计者的提供的注释并不是空穴来风。
TIP 1: Use Winsock2 IOCP-capable functions, such as WSASend and WSARecv, over Win32 file I/O functions, such as WriteFile and ReadFile.
Socket handles from Microsoft-based protocol providers are IFS handles so you can use Win32 file I/O calls with the handle. However, the interactions between the provider and file system involve many kernel/user mode transition, thread context switches, and parameter marshals that result in a significant performance penalty. You should use only Winsock2 IOCP- capable functions with IOCP.
The additional parameter marshals and mode transitions in ReadFile and WriteFile only occur if the provider does not have XP1_IFS_HANDLES bit set in dwServiceFlags1 of its WSAPROTOCOL_INFO structure.
NOTE: These providers have an unavoidable additional mode transition, even in the case of WSASend and WSARecv, although ReadFile and WriteFile will have more of them.

wishel 发表于 2009-11-4 15:43:19

看来更好的办法是改设计,让ACE_WIN32_Asynch_Write_File::write()去override ACE_WIN32_Asynch_Write_Stream::write()
在ACE_WIN32_Asynch_Write_File::write()中只用WriteFile()

iq50 发表于 2009-12-3 09:07:45

个人觉得最好不要随便的乱改库,WriteFile似乎有其缺点的,很多文章有提及,搜一下吧,要么就自己试试。

sevencat 发表于 2009-12-3 12:53:27

增加函数
int ACE_WIN32_Asynch_Write_Stream::shared_write_file (ACE_WIN32_Asynch_Write_Stream_Result *result)
{
        u_long bytes_written;
        if (result->bytes_to_write () > MAXDWORD)
        {
                errno = ERANGE;
                return -1;
        }
        DWORD bytes_to_write = static_cast<DWORD> (result->bytes_to_write ());

        result->set_error (0); // Clear error before starting IO.

        // Initiate the write; Winsock 2 is required for the higher-performing
        // WSASend() function. For Winsock 1, fall back to the slower WriteFile().
        int initiate_result = 0;
        initiate_result = ::WriteFile (result->handle (),
                result->message_block ().rd_ptr (),
                bytes_to_write,
                &bytes_written,
                result);
        if (initiate_result == 1)
                // Immediate success: the OVERLAPPED will still get queued.
                return 0;

        // If initiate failed, check for a bad error.
        ACE_OS::set_errno_to_last_error ();
        switch (errno)
        {
        case ERROR_IO_PENDING:
                // The IO will complete proactively: the OVERLAPPED will still
                // get queued.
                return 0;

        default:
                // Something else went wrong: the OVERLAPPED will not get
                // queued.

                if (ACE::debug ())
                        ACE_DEBUG ((LM_ERROR,
                        ACE_TEXT ("%p\n"),
                        ACE_TEXT ("Initiating write")));
                return -1;
        }
}

修改下面的函数:
int
ACE_WIN32_Asynch_Write_File::write (ACE_Message_Block &message_block,
                                    size_t bytes_to_write,
                                    u_long offset,
                                    u_long offset_high,
                                    const void *act,
                                    int priority,
                                    int signal_number)
{
size_t len = message_block.length ();
if ( bytes_to_write > len )
   bytes_to_write = len;

if ( bytes_to_write == 0 )
    ACE_ERROR_RETURN
      ((LM_ERROR,
      ACE_TEXT ("ACE_WIN32_Asynch_Write_File::write:")
      ACE_TEXT ("Attempt to read 0 bytes\n")),
       -1);

ACE_WIN32_Asynch_Write_File_Result *result = 0;
ACE_NEW_RETURN (result,
                  ACE_WIN32_Asynch_Write_File_Result (this->handler_proxy_,
                                                      this->handle_,
                                                      message_block,
                                                      bytes_to_write,
                                                      act,
                                                      offset,
                                                      offset_high,
                                                      this->win32_proactor_->get_handle (),
                                                      priority,
                                                      signal_number),
                  -1);

// Shared write
int return_val = this->shared_write_file (result);

// Upon errors
if (return_val == -1)
    delete result;

return return_val;
}

其实现在的磁盘速度很快了,我想直接写磁盘 应该也是可以的,在文件中用异步来管理可能意义不大。再不然推到另一个队列线程来写可能更合适。

wishel 发表于 2009-12-3 15:46:06

楼上的解决办法是最好的方式,不过最好把read也改了,体现设计的对称性。
磁盘速度再快跟内存还是差距很大的,现在很多unix上AIO库实现都是只对文件IO有效,如果意义不大这些库就没必要做了。
页: [1]
查看完整版本: 解决ACE_WIN32_Asynch_Write_File::write的问题