找回密码
 用户注册

QQ登录

只需一步,快速开始

查看: 4530|回复: 7

解决ACE_WIN32_Asynch_Write_File::write的问题

[复制链接]
发表于 2009-11-3 15:14:10 | 显示全部楼层 |阅读模式
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()性能好了。
 楼主| 发表于 2009-11-3 15:19:10 | 显示全部楼层
修改代码WIN32_Asynch_IO.cpp中ACE_WIN32_Asynch_Write_Stream::shared_write

原:
  1. #if (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0))
  2.    WSABUF iov;
  3.    iov.buf = result->message_block ().rd_ptr ();
  4.    iov.len = bytes_to_write;
  5.    initiate_result = ::WSASend (reinterpret_cast<SOCKET> (result->handle ()),
  6.                                 &iov,
  7.                                 1,
  8.                                 &bytes_written,
  9.                                 0, // flags
  10.                                 result,
  11.                                 0);
  12.    if (initiate_result == 0)
  13.      // Immediate success: the OVERLAPPED will still get queued.
  14.      return 0;
  15. #else
  16.   initiate_result = ::WriteFile (result->handle (),
  17.                                  result->message_block ().rd_ptr (),
  18.                                  bytes_to_write,
  19.                                  &bytes_written,
  20.                                  result);
  21.   if (initiate_result == 1)
  22.     // Immediate success: the OVERLAPPED will still get queued.
  23.     return 0;
  24. #endif /* ACE_HAS_WINSOCK2 */
复制代码

改为
  1. // #if (defined (ACE_HAS_WINSOCK2) && (ACE_HAS_WINSOCK2 != 0))
  2. //   WSABUF iov;
  3. //   iov.buf = result->message_block ().rd_ptr ();
  4. //   iov.len = bytes_to_write;
  5. //   initiate_result = ::WSASend (reinterpret_cast<SOCKET> (result->handle ()),
  6. //                                &iov,
  7. //                                1,
  8. //                                &bytes_written,
  9. //                                0, // flags
  10. //                                result,
  11. //                                0);
  12. //   if (initiate_result == 0)
  13. //     // Immediate success: the OVERLAPPED will still get queued.
  14. //     return 0;
  15. // #else
  16.   initiate_result = ::WriteFile (result->handle (),
  17.                                  result->message_block ().rd_ptr (),
  18.                                  bytes_to_write,
  19.                                  &bytes_written,
  20.                                  result);
  21.   if (initiate_result == 1)
  22.     // Immediate success: the OVERLAPPED will still get queued.
  23.     return 0;
  24. //#endif /* ACE_HAS_WINSOCK2 */
复制代码

再重新编译ACE即可。
发表于 2009-11-3 16:50:37 | 显示全部楼层
wishel 我按你说的改了(其实昨天我也是这么改的)
改完写文件写不全,只写一点。
发表于 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.
 楼主| 发表于 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()
发表于 2009-12-3 09:07:45 | 显示全部楼层
个人觉得最好不要随便的乱改库,WriteFile似乎有其缺点的,很多文章有提及,搜一下吧,要么就自己试试。
发表于 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;
}

其实现在的磁盘速度很快了,我想直接写磁盘 应该也是可以的,在文件中用异步来管理可能意义不大。再不然推到另一个队列线程来写可能更合适。
 楼主| 发表于 2009-12-3 15:46:06 | 显示全部楼层
楼上的解决办法是最好的方式,不过最好把read也改了,体现设计的对称性。
磁盘速度再快跟内存还是差距很大的,现在很多unix上AIO库实现都是只对文件IO有效,如果意义不大这些库就没必要做了。
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

Archiver|手机版|小黑屋|ACE Developer ( 京ICP备06055248号 )

GMT+8, 2024-12-23 13:27 , Processed in 0.448654 second(s), 6 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表