我正在尝试编写一个函数来将图像转换为Windows控制台的字符和颜色。目前使用700x700像素图像计算大约需要13秒,但是这个时间是不可取的,特别是当我计划使函数更复杂以便考虑字符形状时。
有哪些方法可以加速C ++中的重大计算和循环?我已经推荐了多个线程,SIMD和内联汇编,但是如何使用这些方法改进下面的函数呢?
这是我正在使用的当前代码。
unsigned char characterValues[256] = { 0 };
// This operation can be done ahead of time when the program is started up
{
ResourceInputStream in = ResourceInputStream();
// This image is the font for the console. The background color is black while the foreground color is white
in.open(BMP_FONT, 2); // 2 is for RT_BITMAP, BMP_FONT is a resource
if (in.isOpen()) {
auto bmp = readBitmap(&in, true);
in.close();
for (int x = 0; x < bmp->size.x; x++) {
for (int y = 0; y < bmp->size.y; y++) {
int charIndex = (x / 8) + (y / 12) * 16;
if (bmp->pixels[x][y].r == 255)
characterValues[charIndex]++;
}
}
}
}
// This operation is for asciifying the image
{
FileInputStream in = FileInputStream();
in.open(R"(image-path.bmp)");
if (in.isOpen()) {
auto bmp = readBitmap(&in, false);
in.close();
auto image = /* make default image here */
Point2I imageSize = (Point2I)GMath::ceil((Point2F)bmp->size / Point2F(8.0f, 12.0f));
int totalImageSize = imageSize.x * imageSize.y;
image->resize(imageSize);
auto palette = /* get palette of 16 colors here */
// Iterate through each (character area)
for (int imgx = 0; imgx < imageSize.x; imgx++) {
for (int imgy = 0; imgy < imageSize.y; imgy++) {
// Read image color value
int r = 0, g = 0, b = 0;
int totalRead = 0;
// Read each pixel inside the bounds of a single character
for (int px = 0; px < 8; px++) {
for (int py = 0; py < 12; py++) {
Point2I p = Point2I(imgx * 8 + px, imgy * 12 + py);
if (p < bmp->size) {
r += bmp->pixels[p.x][p.y].r;
g += bmp->pixels[p.x][p.y].g;
b += bmp->pixels[p.x][p.y].b;
totalRead++;
}
}
}
Color imageValue = Color(r / totalRead, g / totalRead, b / totalRead);
// A combo of a character and foreground/background color
Pixel closestPixel = Pixel();
float closestScore = std::numeric_limits<float>().max();
for (int col = 1; col < 255; col++) {
unsigned char f = getFColor(col);
unsigned char b = getBColor(col);
for (int ch = 1; ch < 255; ch++) {
// Calculate values
Color value = Color(
(palette[f].r * characterValues[ch] + palette[b].r * (TOTAL_CHARACTER_VALUE - characterValues[ch])) / TOTAL_CHARACTER_VALUE,
(palette[f].g * characterValues[ch] + palette[b].g * (TOTAL_CHARACTER_VALUE - characterValues[ch])) / TOTAL_CHARACTER_VALUE,
(palette[f].b * characterValues[ch] + palette[b].b * (TOTAL_CHARACTER_VALUE - characterValues[ch])) / TOTAL_CHARACTER_VALUE
);
// Add up score here
float score =
(float)((int)value.r - (int)imageValue.r) * (float)((int)value.r - (int)imageValue.r) +
(float)((int)value.g - (int)imageValue.g) * (float)((int)value.g - (int)imageValue.g) +
(float)((int)value.b - (int)imageValue.b) * (float)((int)value.b - (int)imageValue.b);
if (score < closestScore) {
closestPixel = Pixel((unsigned char)ch, (unsigned char)col);
closestScore = score;
}
}
}
// Set the character/color combo here
}
}
}
}
答案 0 :(得分:2)
你有一个x循环和一个嵌套的y循环,你确定内存中的字节顺序是什么?它可能是但如果它有帮助你总是可以尝试相反的方式。
// could be faster, depending on data structure
for (int y = 0; y < bmp->size.y; y++) {
for (int x = 0; x < bmp->size.x; x++) {
但是由于bmp索引是[x] [y],它看起来像是列的第一个数据,这是奇数。
你的内循环中也存在代价高昂的分歧。你可以在每个循环之外进行任何循环不变的计算:
for (int x = 0; x < bmp->size.x; x++) {
int charIndex_x = (x / 8);
for (int y = 0; y < bmp->size.y; y++) {
int charIndex = charIndex_x + (y / 12) * 16;
// other stuff
它仍然可以进一步改进,但你只是避免在为256x256位图执行此操作时执行近65536次除法操作。
另外,在你的内循环中有一个2D数组解除引用,这些是昂贵的操作。您可以记录指向列开头的指针,然后递增指针:
for (int x = 0; x < bmp->size.x; x++) {
int charIndex_x = (x / 8);
auto current_pixel = &bmp->pixels[x][0];
for (int y = 0; y < bmp->size.y; y++) {
int charIndex = charIndex_x + (y / 12) * 16;
if (*current_pixel.r == 255)
characterValues[charIndex]++;
++current_pixel;
并在内循环中增加它。事实上,您可以在x循环外部移动current_pixel设置,但我已经遇到较慢的情况,因为它必须在内存中保留更多变量。通常,如果可能,您需要内部循环中的局部变量。在循环外部移动计算会加快速度,但会占用更多CPU内存,这意味着由于更多存储值的存储,它可能会变慢。
最后要注意的是,每次通过内循环,你都要检查y值是否小于&#34; bmp-&gt; size.y&#34;这包括查找bmp然后引用大小,然后引用size.y,这是三个操作,对于256x256位图发生65536次。您可以在需要之前在本地变量中记录y大小:
for (int x = 0; x < bmp->size.x; x++) {
int charIndex_x = (x / 8);
auto current_pixel = &bmp->pixels[x][0];
int bmp_size_y = bmp->size.y;
for (int y = 0; y < bmp_size.y; y++) {
int charIndex = charIndex_x + (y / 12) * 16;
if (*current_pixel.r == 255)
characterValues[charIndex]++;
++current_pixel;
你可以将它完全移到x循环之外,以避免将值设置256次,因为bmp-&gt; size.y永远不会改变,但是对它的保存非常小,甚至可能会减慢它的速度,因为它会耗尽和额外注册,这可能意味着程序需要在内存中处理更多内容。
CPU内存与Windows PC上的虚拟内存类似。如果使用太多,事情会变慢,因为它会将内容分页到磁盘,但在内存中有更多内容也可以加快速度,因为它不需要经常从磁盘查找内容。编码是类似的,因为局部变量可以存储在CPU中,避免必须从内存中查找它们,但太多的局部变量会使CPU过载,这意味着它需要像虚拟内存一样处理它们确实。因此,将局部变量设为&#34; local&#34;尽可能避免过度使用它们。您应该始终对您所做的任何更改进行分析,以确定它们是否真的有用。
~~~
至于你的另一个循环,你在内循环中有许多复杂的重复计算:
bmp->pixels[p.x][p.y]
计算三次,这包括指针解引用,两个成员dereferces(p.x和p.y)然后是2D数组解除引用(最多是乘法和加法,然后是指针解除引用)。那里至少有6个原子计算,只是为了每次获得对该像素的引用。
相反,你可以去:
auto current_pixel = bmp->pixels[p.x][p.y];
更好的是,您正在计算Point2I,然后检查它的x和y值是否在bmp大小内。您根本不需要Point2I,只需计算x和y尺寸,然后分别与bmp x和y尺寸进行比较。
计算外部循环中的x边界,对那里的x执行if-check,如果x超出界限,则完全避免碰到内部循环。将其与避免在内循环内创建或索引结构相结合,你得到:
for (int px = 0; px < 8; px++) {
int p_x = imgx * 8 + px;
if(p_x < bmp->size.x) {
for (int py = 0; py < 12; py++) {
int p_y = imgy * 12 + py;
if (p_y < bmp->size.y) {
auto pixel = bmp->pixels[p_x][p_y];
r += pixel.r;
g += pixel.g;
b += pixel.b;
totalRead++;
}
}
}
}
答案 1 :(得分:1)
for (int x = 0; x < bmp->size.x; x++) {
for (int y = 0; y < bmp->size.y; y++) {
将这两个循环分别设置为最高值,即bmp->size.x-1
和bmp->size.y-1
,并将它们 down 运行为零。这样,您只需每循环一次评估边界条件,而不是每次迭代。
int charIndex = (x / 8) + (y / 12) * 16;
除非您要使用它,否则不要计算它,即将其放入以下if
块中。