python - 减少巨大列表生成的执行时间

标签 python multithreading python-3.5 execution-time

我对Python相当陌生,我正在尝试编写一些巨大的列表(里面有随机字母)。实际上,在我的机器上处理 2,000,000 行代码大约需要 75 - 80 秒。

import timeit
import random, string

global_tab     = []
global_nb_loop = 2000000

print("Generate %d lines" % global_nb_loop)
global_tab = []
for x in range(global_nb_loop):
    global_tab.append(("".join( [random.choice(string.ascii_letters) for i in range(15)] ), "".join( [random.choice(string.digits) for i in range(2)])))
print("%d lines generated" % len(global_tab))

Linux time 命令的结果:

$ time python3 DEV/PyETL/generateList.py 
Generate 2000000 lines
2000000 lines generated

real    1m16.844s
user    1m16.609s
sys 0m0.203s

当监控系统资源时,我感到很惊讶,只有 1 个核心处于 100%,而不是像我也测试过的 Windows 计算机上的 4 个核心。

当然,我尝试过应用一些线程,但我遇到了一个问题:它比在单核上运行需要更多的时间。也许线程不是解决方案,或者我可能错误地使用了它们。

这是新代码:

import random, string
import threading

global_tab         = []
global_nb_threads  = 4
global_nb_loop     = 2000000


threadLock         = threading.Lock()

class generateList(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run(self):
        global global_tab
        self.tab = []

        print("[%s] Generate %d lines" % (self.name, int(global_nb_loop/global_nb_threads)))
        # divide desirated lines with number of threads
        for x in range(int(global_nb_loop/global_nb_threads)):
            self.tab.append(("".join( [random.choice(string.ascii_letters) for i in range(15)] ), "".join( [random.choice(string.digits) for i in range(2)])))

        threadLock.acquire()
        global_tab += self.tab
        threadLock.release()
        del self.tab
        print("[%s] %d lines in list" % (self.name, len(global_tab)))


for i in range(global_nb_threads):
    # Create threads
    t = generateList("Thread-" + str(i))
    # Start
    t.start()

for i in range(global_nb_threads):
    # Wait for threads end
    t.join()

执行:

$ time python3 DEV/PyETL/generateListThreads.py 
[Thread-0] Generate 500000 lines
[Thread-1] Generate 500000 lines
[Thread-2] Generate 500000 lines
[Thread-3] Generate 500000 lines
[Thread-3] 500000 lines in list
[Thread-0] 1000000 lines in list
[Thread-2] 1500000 lines in list
[Thread-1] 2000000 lines in list    
real    1m40.858s
user    1m41.208s
sys 0m0.916s

比 1 个核心 100% 多出 32 秒,但监控显示 8 个核心同时处于 20 - 40% 负载。

由于所有线程同时工作,生成的行数较少,并且仅在更新全局变量时进行同步,因此执行时间不应该低于单核吗?

最佳答案

我很确定你的锁是不必要的,而且会减慢你的速度。 (编辑:实际上,我只是注意到锁是在大部分工作完成之后使用的,所以并不真正相关。)

global_tab += self.tab (我认为)通过 Python GIL 是原子的。 (实际上,this 仅声明 list.extend(),因此请使用它。这是另一个引用:Are lists thread safe?

或者,我会尝试使用大块大小的multiprocessing.imap_unordered。缺点是结果是通过流发送的,但您的随机字符串处理可能会掩盖这一点。

import multiprocessing
import random
import string

def randomword(x):
    return ''.join(random.choice(string.ascii_letters) for i in range(15))

pool = multiprocessing.Pool(8)
results = pool.imap_unordered(randomword, range(100))
print([r for r in results])

对于 200 万个字符串(我将其更改为打印长度):

$ time python r.py                                                                 
2000000

real    0m38.305s
user    1m31.717s
sys     0m25.853s

我还尝试清理一下你的版本并得到:

$ time python rr.py 
[Thread-0] Generate 250000 lines
[Thread-1] Generate 250000 lines
[Thread-2] Generate 250000 lines
[Thread-3] Generate 250000 lines
[Thread-4] Generate 250000 lines
[Thread-5] Generate 250000 lines
[Thread-6] Generate 250000 lines
[Thread-7] Generate 250000 lines
[Thread-4] 250000 lines in list
[Thread-1] 500000 lines in list
[Thread-7] 750000 lines in list
[Thread-0] 1000000 lines in list
[Thread-6] 1250000 lines in list
[Thread-2] 1500000 lines in list
[Thread-3] 1750000 lines in list
[Thread-5] 2000000 lines in list

real    0m22.113s
user    0m24.969s
sys     0m5.537s

一些重大变化:

  • 在大范围上使用xrange()(啊,python3 已经这样做了。)
  • 移除线程锁
  • 在全局上使用extend()

(顺便说一句,当仅附加到 global_tab 并忽略临时列表时,我的结果大致相同。)

import random, string
import threading

global_tab         = []
global_nb_threads  = 8
global_nb_loop     = 2000000

class generateList(threading.Thread):
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name

    def run(self):
        global global_tab
        self.tab = []

        print("[%s] Generate %d lines" % (self.name, int(global_nb_loop/global_nb_threads)))
        for x in range(int(global_nb_loop/global_nb_threads)):
            self.tab.append(("".join( [random.choice(string.ascii_letters) for i in range(15)] ), "".join( [random.choice(string.digits) for i in range(2)])))

        global_tab.extend(self.tab)
        print("[%s] %d lines in list" % (self.name, len(global_tab)))


for i in range(global_nb_threads):
    t = generateList("Thread-" + str(i))
    t.start()

for i in range(global_nb_threads):
    t.join()

...但是,单线程仍然稍快一些,为 16 秒。

如果我调整多处理,我可以将其缩短至 6 秒:

size = 2000000
processes = 8
pool = multiprocessing.Pool(processes)
results = [r for r in pool.imap_unordered(randomword, range(size), chunksize=int(size/processes))]
print(len(results))

输出:

$ time python r.py                                                                 
2000000

real    0m5.713s
user    0m35.594s
sys     0m0.546s

...所以我认为这就是我的最终答案:使用多处理

关于python - 减少巨大列表生成的执行时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37890199/

相关文章:

python - 可以在 numpy 中形成 ndarray 对角线的 View 吗

python - 打印随机文本中的计数(单词出现次数)(Print hackerearth)

python - 如何在cygwin中使用conda

Python Tkinter 小窗口在主窗口之前弹出

python - 我如何使用 Mean_Squared_Error (Keras) 知道我的神经网络是否表现良好

multithreading - 多线程:读取/写入管道

c# - 后台任务有时能够更新 UI?

multithreading - 在顺序执行的线程中使用ArrayBuffer?

windows-10 - 安装 glove-python-> link.exe 时出错,退出状态为 1181

python - pandas.read_csv 与其他用于将 CSV 加载到 Postgres 数据库中的 csv 库