【问题标题】:Optimizing loop that has QString manipulations [closed]优化具有 QString 操作的循环[关闭]
【发布时间】:2015-08-18 21:50:55
【问题描述】:

在我的项目中,我编写了一个函数来解压缩 QString,该 QString 使用我在单独函数中编写的非常基本的压缩格式进行压缩。但是经过一些测试,我发现这个函数是导致严重减速的原因,因为它在巨大的 QString 上运行并被调用超过 2900 次。

我一直在尝试更改此功能以使其运行得更快。我曾尝试过使用 QStringRef,但效果不佳(我可能做错了)。 QByteArrays 和 QByteRefs 很难使用和检查值 (imo)。

真的需要一些帮助来优化此功能,以便它运行FAST!尽可能快!我相信 .mid 的不断调用会减慢速度,但我只是不知道任何其他方式来读取/写入字节。

编辑:更好的问题,在减压功能方面我是否缺少一种常见的做法?我稍后在同一个程序中使用 zlib,它比我在下面编写的这个简单函数压缩得更快。这是为什么? zlib 有什么不同?

提前感谢您的宝贵时间。 :)


这是一个非常小的压缩 QString 的样子:

//Compressed
//This QString is just a hexadecimal representation of a QByteArray
//
QString com("010203ff0504ff0a05ff00ff01ff02ff0306);

还有,解压后相同的 QString 如下:

//Decompressed
QString decom("0102030404040404040505050505050505050505ffffffffffff06060606);

很抱歉,如果您不立即理解格式……那没关系。也许这会有所帮助:

-a byte with "ff" tells us we're about to decompress
-the byte after "ff" is the number of times to repeat the NEXT byte + 1
-UNLESS that number is 0, 1, or 2, then "ff" is the value to be repeated

Examples:
-"010203" decompressed is "010203"

-"ff0401" decompressed is "0101010101"

-"ff02" decompressed is "ffffff"

这是我写的解压函数:

int HexToIntS(QString num_hex)  //converts the byte to a number
{
    uint num_uint;
    bool ok;
    num_uint = num_hex.toUInt(&ok,16);
    return (int)num_uint;
}
void Decompress(QString com, QString &decom)
{
    QString c;                 //current byte
    QString n;                 //new/next byte
    int bytePos(0);            //current position in QString
    int byteRepeat;            //number of times to repeat byte n

    c = com.mid(bytePos, 2);   //get first byte (01)
    decom.clear();             //clear decom just in case it had values prior

    do
    {
        bytePos = bytePos + 2;      //move the current position to the next byte
        if(c == "ff")               //is decompression happening?
        {
            c = com.mid(bytePos, 2);   //current byte is now the "next" byte
            byteRepeat = HexToIntS(c); //c tells us how many times the NEXT byte needs to be repeated

            if(byteRepeat <= 2)        //if c's value is <= 2... then ff is the value
            {
                n = "ff";              //new byte is just ff
                bytePos = bytePos + 2; //update the current position
            }
            else                       //if not, then c is the number of times the NEXT byte should be appended
            {
                n = com.mid(bytePos + 2, 2); //new byte is the NEXT byte
                bytePos = bytePos + 4;       //update the current position
            }

            for(int j = 0; j<=byteRepeat; j++)//append n the correct number of times
                decom.append(n);
        }
        else                   //guess we're not decompressing, so just append c
            decom.append(c);
        c = com.mid(bytePos, 2);   //get the new current byte
    }while(bytePos < com.length());  //stop when all bytes were read
}

当前基于您的 cmets 优化的函数:(仅在调试模式下快 5%-10%)

void Decompress2(const QString com, QString &decom)
{
    QStringRef c;
    QString n;
    int bytePos(0);
    int byteRepeat;

    c = com.midRef(bytePos, 2);
    decom.clear();

    do
    {
        bytePos = bytePos + 2;
        if(c == "ff")
        {
            c = com.midRef(bytePos, 2);
            byteRepeat = c.toString().toInt(0,16);

            if(byteRepeat <= 2)
            {
                n = "ff";
                bytePos = bytePos + 2;
            }
            else
            {
                n = com.mid(bytePos + 2, 2);
                bytePos = bytePos + 4;
            }

            for(int j = 0; j<=byteRepeat; j++)
                decom.append(n);
        }
        else
            decom.append(c);
        c = com.midRef(bytePos, 2);
    }while(bytePos < com.length());
}

【问题讨论】:

  • 您可以使用分析器来查看函数的哪个部分“消耗”了很多时间,并尝试使用运行速度更快的等效代码来优化/更改/替换该部分。
  • 是的,我正在调查。接下来我将很快为我的整个工作线程执行此操作。我整晚都在评论我的程序的一部分,这个功能无疑会导致巨大的减速。你觉得有什么马上就错了吗?
  • 那么您是否尝试使用 midRef 代替?
  • 我投票结束这个问题,因为它是关于优化工作代码的,属于Code Review
  • 顺便说一句,永远不要在调试模式下测试性能。始终处于优化状态。

标签: c++ qt optimization qstring


【解决方案1】:

您不应该将字节数组视为字符串。这很愚蠢,正如你所指出的,死得很慢。请改用原始字节值并对其进行操作。

我知道我不应该为其他人编写代码,但我绝对没有更好的事情可做,所以这里直接使用 C++。我知道您使用的是 Qt,并且我相当肯定以下大部分代码在 Qt 的 ByteArray 方面都有一些等价物,但如果纯 C++ 不是一个选项,那么您可以弄清楚这一点。

#include <vector>
#include <cstdint>
#include <iomanip>
#include <iostream>

std::vector<std::uint8_t> decompress(const std::vector<std::uint8_t>& com)
{
  std::vector<std::uint8_t> decom;
  decom.reserve(com.size()); // a conservative estimate of the required size

  for(auto it = begin(com); it != end(com); ++it)
  {
    if(*it == 0xff)
    {
      ++it;
      if(it != end(com))
      {
        std::uint8_t number_of_repeats = *it;
        if(number_of_repeats <= 2)
        {
          std::fill_n(std::back_inserter(decom), number_of_repeats, 0xff);
          continue;
        }
        else
        {
          ++it;
          if(it != end(com))
            std::fill_n(std::back_inserter(decom), number_of_repeats, *it);
          else
            throw 42; // handle error in some way
        }
      }
      else 
        throw 42; // handle error in some way
    }
    else
      decom.push_back(*it);
  }
  return decom;
}
int main()
{
  std::vector<std::uint8_t> com{0x01, 0x02, 0x03, 0xff, 0x05, 0x04, 0xff, 0x0a, 0x05, 0xff, 0x00, 0xff, 0x01, 0xff, 0x02, 0xff, 0x03, 0x06};


  for(const auto& value : com)
    std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned short>(value) << ' ';
  std::cout << '\n';
  auto result = decompress(com);

  for(const auto& value : result)
    std::cout << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned short>(value) << ' ';
}

Live demo here。我对此代码的正确性、效率或其他可用性不承担任何责任。不到五分钟就写完了。

请注意,我认为您在长示例中的解压缩字符串是错误的。按照你的规定,应该是

01 02 03 04 04 04 04 04 05 05 05 05 05 05 05 05 05 05 ff ff ff 06 06 06

从后面开始,06 重复 3 次,然后 2 次 ff,然后 1 次 ff,然后 0 次 ff,然后是其余的。

【讨论】:

  • 非常感谢您的回答超出预期。我将把它与我构建的新算法结合起来,让你知道它是如何进行的。不,减压是正确的。要重复的数字是下一个字节 + 1。所以 ff0306 将是 06060606 (03 + 1)
  • 啊,看来我错过了加 1 :)。非常欢迎你!如果您认为我的回答值得,请不要忘记支持/接受;)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-29
  • 2020-10-22
  • 2016-10-09
  • 2021-12-12
  • 2016-02-18
相关资源
最近更新 更多