处理像素时线程性能下降
Threading Performance Degradation when Handling Pixels
当我的 Python 脚本生成一个线程并且 运行 执行以下代码时,运行 时间为 0.8 秒。当它生成五个线程并 运行s 代码时,运行 时间约为 5.0 秒。
显然,即使有 5 或 15 个线程,我也希望代码在 ~0.8 秒内完成 运行。为什么会这样?我已经使用线程来改善程序其他部分的 运行 时间,但由于某种原因,它在这里成为瓶颈。另外,我从来没有生成超过 60 个线程,所以这应该不会影响性能。
# Open the image
imgx = Image.open(imgName)
imgx = imgx.convert("RGBA")
pix = imgx.load()
# Adjust dark pixels to black or white
for y in xrange(imgx.size[1]):
for x in xrange(imgx.size[0]):
# Get RGBA values for a pixel
(first, second, third, alpha) = pix[x, y]
# Ajust the RGBA values accordingly
if (first > threshold) or (second > threshold) or (third > threshold):
first = 255
second = 255
third = 255
alpha = 255
else:
first = 0
second = 0
third = 0
alpha = 255
# Set new pixel values
pix[x, y] = (first, second, third, alpha)
Python 解释器有一个全局锁(称为全局解释器锁或 GIL),可防止纯 Python 代码在多个线程中并发 运行。
在 Python 循环中单独迭代像素无论如何都是非常低效的。您应该使用 Numpy 的矢量化函数,该函数可以全局作用于数组。这在单个线程中会快得多,并且具有 Numpy 在数组操作期间释放 GIL 的额外优势,因此它们实际上可以在多个线程中并行发生。
您可能甚至不需要为此应用程序使用线程。使用多进程比使用线程要精妙得多。
Numpy 代码大致相当于您编写的内容
img = Image.open(imgName).convert("RGBA")
arr = numpy.array(img)
# Split the channels of the image by rotating the axes
r, g, b, a = arr.transpose(2, 0, 1)
# Create Boolean array: True means pixel is above threshold
bw = (r > threshold) | (g > threshold) | (b > threshold)
# Set R, G and B channel to the 255 times the B/W array
arr[:, :, :2] = 255 * bw[:, :, numpy.newaxis]
# Set alpha channel to 255
arr[:, :, 3] = 255
# Create new PIL image from array
new_img = Image.fromarray(arr)
当我的 Python 脚本生成一个线程并且 运行 执行以下代码时,运行 时间为 0.8 秒。当它生成五个线程并 运行s 代码时,运行 时间约为 5.0 秒。
显然,即使有 5 或 15 个线程,我也希望代码在 ~0.8 秒内完成 运行。为什么会这样?我已经使用线程来改善程序其他部分的 运行 时间,但由于某种原因,它在这里成为瓶颈。另外,我从来没有生成超过 60 个线程,所以这应该不会影响性能。
# Open the image
imgx = Image.open(imgName)
imgx = imgx.convert("RGBA")
pix = imgx.load()
# Adjust dark pixels to black or white
for y in xrange(imgx.size[1]):
for x in xrange(imgx.size[0]):
# Get RGBA values for a pixel
(first, second, third, alpha) = pix[x, y]
# Ajust the RGBA values accordingly
if (first > threshold) or (second > threshold) or (third > threshold):
first = 255
second = 255
third = 255
alpha = 255
else:
first = 0
second = 0
third = 0
alpha = 255
# Set new pixel values
pix[x, y] = (first, second, third, alpha)
Python 解释器有一个全局锁(称为全局解释器锁或 GIL),可防止纯 Python 代码在多个线程中并发 运行。
在 Python 循环中单独迭代像素无论如何都是非常低效的。您应该使用 Numpy 的矢量化函数,该函数可以全局作用于数组。这在单个线程中会快得多,并且具有 Numpy 在数组操作期间释放 GIL 的额外优势,因此它们实际上可以在多个线程中并行发生。
您可能甚至不需要为此应用程序使用线程。使用多进程比使用线程要精妙得多。
Numpy 代码大致相当于您编写的内容
img = Image.open(imgName).convert("RGBA")
arr = numpy.array(img)
# Split the channels of the image by rotating the axes
r, g, b, a = arr.transpose(2, 0, 1)
# Create Boolean array: True means pixel is above threshold
bw = (r > threshold) | (g > threshold) | (b > threshold)
# Set R, G and B channel to the 255 times the B/W array
arr[:, :, :2] = 255 * bw[:, :, numpy.newaxis]
# Set alpha channel to 255
arr[:, :, 3] = 255
# Create new PIL image from array
new_img = Image.fromarray(arr)