c++ - 优化具有 QString 操作的循环

标签 c++ qt optimization qstring

<分区>

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

我一直在尝试更改此功能以使其运行得更快。我已经尝试过 QStringRef,但没有什么好结果(我可能做错了)。 QByteArrays 和 QByteRefs 很难使用和检查值(imo)。

真的需要一些帮助来优化这个函数,让它快!尽可能快地运行!我相信 .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
}

当前根据您的评论优化的函数:(仅在 Debug模式下快 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());
}

最佳答案

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

我知道我不应该为其他人编写代码,但我绝对没有更好的事可做,所以这里直接使用 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,然后是其余的。

关于c++ - 优化具有 QString 操作的循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30641225/

相关文章:

JavaScript优化: Caching math functions globally

c++ - 如何在我的命名空间中使用 LoadString 宏?

c++ - std::any_cast() 和 std::get_if(std::variant) 是否绝对有必要将指针作为参数?

c++ - 在 C++ 中进行碰撞检测的多重分派(dispatch)?

c++ - 我可以在所有 MSVC >= 2013 上安全地使用哪些 SFINAE 技巧?

qt - 如何在设计器中从多个小部件制作一个小部件

c++ - 如何拥有一个 QNetworkAccessManager 实例?

haskell - 当存在一些递归情况时保持内联的潜力

c++ - 自定义 QListView 中项目的图标和文本位置的正确方法是什么?

java - Java 是否在运行时优化了字符串的创建?