【问题标题】:Optimizing speed of read/write function on hard disk using multithreading使用多线程优化硬盘读写功能的速度
【发布时间】:2016-09-27 19:21:03
【问题描述】:

我正在尝试读取 -> 加密/解密 -> 逐个扇区地在卷上写入原始数据。我需要以某种方式优化时间,因为仅 2GB 的卷需要 2 个多小时(我的卷 G:这里)。我尝试使用线程来处理时间问题,但没有显着的效果。该程序需要在日常使用的 PC 上运行。

每个线程在与其他线程不同的扇区块上运行。

我知道的事情:

  1. 写时不能读(所以我处理了临界区问题)
  2. 线程中的读/写会因寻道时间等而减少时间。
  3. 由于延迟(由读/写头来回移动引起。(给出错误 DEVICE_NOT_READY)),太多线程可能无法完成任务

我想问:

  1. 我能否利用线程同时在两个不同的扇区上进行读写操作(因为我知道磁盘操作是使用已回答 here 的调度算法执行的)?
  2. 为什么程序在一个线程(在一个扇区上)写入期间,其他线程尝试读取(在另一个扇区上)时会产生错误?
  3. 您会建议哪些修改来缩短时间?

代码:

DWORD WINAPI EncryptSectorBlock(LPVOID lpParam)
{
    PTARGS args = (PTARGS)lpParam;
    static unsigned char buffer[SECTOR_SIZE];

    printf("Thread No:%i start:%i  end:%i\n ", args->thread_id, args->sector_start, args->sector_end);

    for (int i = args->sector_start; i <= args->sector_end; i++)
    {
        //Entering critical section of the code. The Other Threads Would be first spin with 65536 loops and then set to
        // sleep until the worker threads releases the lock on critical section

        if (ReadSector(args->read, buffer, SECTOR_SIZE, i) != 1)
            printf("Thread: %i. Error reading sector %i\n",args->thread_id, i);
        else
            printf("Thread: %i. Read Sector %i Successfully\n", args->thread_id, i);

        xts_encrypt_sector(buffer, i, SECTOR_SIZE, &(args->ctx));

        //Critical Section starts
        EnterCriticalSection(&CriticalSection);
        if (WriteSector(args->write, buffer, SECTOR_SIZE, i) != 1)
            printf("Thread: %i. Error writing sector %i\n",args->thread_id ,i);
        else
            printf("Thread: %i. Wrote Sector %i Successfully\n", args->thread_id, i);
        //Critical Section Ends
        LeaveCriticalSection(&CriticalSection);


        //init to zero every time in case a sector is failed to be read or write, so it can recover from fault 
        //This may break in future.. Not proper mechanism to recover from   fault. just a hack.
         memset(buffer, 0, SECTOR_SIZE);
    }
    return 0;
  }

INT_RETURN EncryptFullVolume(wchar_t* volume, unsigned char* key)
{   
       //init variables here

            for (int i = 0; i < MAX_Threads; i++)
        {
            ArgsDataArray[i] = (PTARGS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TARGS));

            if (ArgsDataArray[i] == NULL)
            {
                // If the array allocation fails, the system is out of memory
                // so there is no point in trying to print an error message.
                // Just terminate execution.
                ret = EXIT_FAILURE;
            }
            else
            {
                // Generate unique data for each thread to work with here

               // Create the thread to begin execution on its own.
                hThreadArray[i] = CreateThread(
                    NULL,                   // default security attributes
                    0,                      // use default stack size  
                    EncryptSectorBlock,     // thread function name
                    ArgsDataArray[i],          // argument to thread function 
                    0,                      // use default creation flags 
                    &ThreadIdArray[i]);   // returns the thread identifier 

                if (hThreadArray[i] == NULL)
                {
                    ret = EXIT_FAILURE;
                }

                sector_offset += sectors_per_thread;
            }

        } // End of main thread creation loop.

            // Wait until all threads have terminated.

        DWORD result = WaitForMultipleObjects(MAX_Threads, hThreadArray, TRUE, INFINITE);


        // Free all the dynamically allocated structures
    }
    }
   return ret;

}

【问题讨论】:

  • 太宽泛了。分析您的代码,识别热点,然后优化它们。但首先检查您的设备是否确实可以以更高的速率传输。
  • 是的,与我使用此程序获得的效率相比,我的硬盘可以以更高的速度传输
  • 您的意思是 2GB?不是 2TB?
  • 我用于测试的卷是2GB...显然它可以是任何大小。
  • 您已经回答了 我知道的事情部分下的问题2,第一个要点。其余的只是有缺陷的逻辑。出租车不会因为你添加更多司机而跑得更快。

标签: c multithreading winapi


【解决方案1】:

你的第 2 点是不正确的。

对于普通磁盘,最好的访问模式是大的顺序读取,而不必因为多个线程争用磁盘时间而来回跳过(这会浪费查找时间);此外,顺序读取与操作系统和磁盘本身完成的预读缓存配合得很好。

您可能需要多线程来处理您从磁盘读取的数据,但考虑到在现代 PC 上,您的处理器比磁盘要快得多,因此您的任务将是IO 绑定。如果我是你,我只会有一个线程在大块中执行异步顺序 IO(一次考虑 4 MB 数据),在等待磁盘执行它的操作时加密数据。另一种可能性是用两个线程(一个做 IO,一个加密)和同步 IO 做本质上相同的事情。

【讨论】:

  • 中间有一个操作系统,确实很难有一个特定的应用程序模式。无论如何,单个现代 x86/64 CPU 不应该成为瓶颈,但这取决于算法。
  • @Olaf:即使是这样,在这种情况下,您也可以旋转多个线程来处理大缓冲区的预定块。但在我看来,在多个线程之间的扇区级多路复用随机读写似乎是最糟糕的访问模式。
  • @MatteoItalia 我的加密取决于存储在缓冲区中的 IO 输出。在 IO 以完成状态返回之前,我无法进行加密。请原谅我问,但我无法理解您回答中的这一行:在等待磁盘执行其操作时加密数据。而加密取决于 IO 的完全成功返回。
  • @U.Ahmad:你保留了三个缓冲区:一个输入,一个工作,一个输出。在每次迭代中,您都对工作缓冲区中的数据进行加密;完成后,检查之前的异步写入是否完成;如果它仍在工作,请等待(进一步指责磁盘没有意义)直到它完成。完成后,将数据从工作缓冲区复制到写入缓冲区,并发出异步 IO 请求。然后,检查读取缓冲区中的异步读取是否完成;如果没有,你等着。是的,您将其数据复制到工作缓冲区中,发出异步读取请求...
  • ... 用于下一个扇区,然后继续加密。像这样继续下去,直到完成必须处理的扇区。当然,所有这些副本都可以避免——这里我说的是“缓冲区之间的复制”,但这只是重新标记它们的问题(一些指针交换并且你已经设置好了)。
猜你喜欢
  • 2011-07-16
  • 2020-09-02
  • 1970-01-01
  • 2013-08-22
  • 2017-09-17
  • 2014-01-23
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
相关资源
最近更新 更多