使用 Python 向 LMDB 写入数据非常慢
Writing data to LMDB with Python very slow
使用 Caffe 创建用于训练的数据集 我都尝试过使用 HDF5 和 LMDB。但是,创建 LMDB 非常慢,甚至比 HDF5 还慢。我正在尝试写 ~20,000 张图片。
我是不是做错了什么?有什么我不知道的吗?
这是我创建 LMDB 的代码:
DB_KEY_FORMAT = "{:0>10d}"
db = lmdb.open(path, map_size=int(1e12))
curr_idx = 0
commit_size = 1000
for curr_commit_idx in range(0, num_data, commit_size):
with in_db_data.begin(write=True) as in_txn:
for i in range(curr_commit_idx, min(curr_commit_idx + commit_size, num_data)):
d, l = data[i], labels[i]
im_dat = caffe.io.array_to_datum(d.astype(float), label=int(l))
key = DB_KEY_FORMAT.format(curr_idx)
in_txn.put(key, im_dat.SerializeToString())
curr_idx += 1
db.close()
如您所见,我正在为每 1,000 张图像创建一个事务,因为我认为为每个图像创建一个事务会产生开销,但这似乎不会对性能产生太大影响。
试试这个:
DB_KEY_FORMAT = "{:0>10d}"
db = lmdb.open(path, map_size=int(1e12))
curr_idx = 0
commit_size = 1000
with in_db_data.begin(write=True) as in_txn:
for curr_commit_idx in range(0, num_data, commit_size):
for i in range(curr_commit_idx, min(curr_commit_idx + commit_size, num_data)):
d, l = data[i], labels[i]
im_dat = caffe.io.array_to_datum(d.astype(float), label=int(l))
key = DB_KEY_FORMAT.format(curr_idx)
in_txn.put(key, im_dat.SerializeToString())
curr_idx += 1
db.close()
代码
with in_db_data.begin(write=True) as in_txn:
需要很多时间。
根据我的经验,我从 Python 将 Caffe 数据写入 Ubuntu 上的 ext4 硬盘上 50-100 毫秒写入 LMDB。 这就是为什么我使用 tmpfs(RAM 磁盘 功能内置于 Linux)并在 0.07 左右完成这些写入毫秒。你可以在你的 ramdisk 上制作更小的数据库,然后将它们复制到硬盘上,然后在所有这些数据库上进行训练。由于我有 64 GB 的 RAM,我正在制作大约 20-40GB 的内存。
一些代码可以帮助你们动态创建、填充 LMDB 并将其移动到存储中。随意编辑它以适合您的情况。它应该可以为您节省一些时间来了解 LMDB 和文件操作在 Python.
中的工作原理。
import shutil
import lmdb
import random
def move_db():
global image_db
image_db.close();
rnd = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5))
shutil.move( fold + 'ram/train_images', '/storage/lmdb/'+rnd)
open_db()
def open_db():
global image_db
image_db = lmdb.open(os.path.join(fold, 'ram/train_images'),
map_async=True,
max_dbs=0)
def write_to_lmdb(db, key, value):
"""
Write (key,value) to db
"""
success = False
while not success:
txn = db.begin(write=True)
try:
txn.put(key, value)
txn.commit()
success = True
except lmdb.MapFullError:
txn.abort()
# double the map_size
curr_limit = db.info()['map_size']
new_limit = curr_limit*2
print '>>> Doubling LMDB map size to %sMB ...' % (new_limit>>20,)
db.set_mapsize(new_limit) # double it
...
image_datum = caffe.io.array_to_datum( transformed_image, label )
write_to_lmdb(image_db, str(itr), image_datum.SerializeToString())
LMDB 写入对顺序非常敏感 - 如果您可以在插入之前对数据进行排序,速度将显着提高
我做了一个小基准来说明 Ophir 的观点:
机器:
RasPi 4B - 超频至 1.75 GHz,4GB,RasperryPi OS,SSD OS
代码:
def insert_lmdb(fsobj, transaction):
transaction.put(key=str(fsobj).encode("utf-8", "ignore"), value=generate_hash_from_file(fsobj).hexdigest().encode("utf-8", "ignore"))
list_f = list_files(FOLDER)
print(f"\n> Insert results in lmdb <")
list_f = Directory(path=DIR_ECTORY, use_hash=False, hash_from_content=False).lists["files"]
# list_f = sorted(list_f) # Run only in the 'sorted' case.
st = timeit.default_timer()
env = lmdb.open(path=DB_NAME)
with env.begin(write=True) as txn:
for i in list_f:
insert_lmdb(i, transaction=txn)
average = (timeit.default_timer() - st)*1000000/records
print(f"Test repeated {TIMES} times.\nNumber of files: {records}\nAverage time: {round(average, 3)} us or {round(1000000/average/1000, 3)}k inserts/sec")
结果:
不排序:[=37=]
> Insert results in lmdb <
Test repeated 50000 times.
Number of files: 363
Average time: 84 us or 12k inserts/sec
有排序:
> Insert results in lmdb <
Test repeated 50000 times.
Number of files: 363
Average time: 18.5 us or 54k inserts/sec
排序使写入速度提高了 4.5 倍,仅增加一行代码就不错了:)。
使用 Caffe 创建用于训练的数据集 我都尝试过使用 HDF5 和 LMDB。但是,创建 LMDB 非常慢,甚至比 HDF5 还慢。我正在尝试写 ~20,000 张图片。
我是不是做错了什么?有什么我不知道的吗?
这是我创建 LMDB 的代码:
DB_KEY_FORMAT = "{:0>10d}"
db = lmdb.open(path, map_size=int(1e12))
curr_idx = 0
commit_size = 1000
for curr_commit_idx in range(0, num_data, commit_size):
with in_db_data.begin(write=True) as in_txn:
for i in range(curr_commit_idx, min(curr_commit_idx + commit_size, num_data)):
d, l = data[i], labels[i]
im_dat = caffe.io.array_to_datum(d.astype(float), label=int(l))
key = DB_KEY_FORMAT.format(curr_idx)
in_txn.put(key, im_dat.SerializeToString())
curr_idx += 1
db.close()
如您所见,我正在为每 1,000 张图像创建一个事务,因为我认为为每个图像创建一个事务会产生开销,但这似乎不会对性能产生太大影响。
试试这个:
DB_KEY_FORMAT = "{:0>10d}"
db = lmdb.open(path, map_size=int(1e12))
curr_idx = 0
commit_size = 1000
with in_db_data.begin(write=True) as in_txn:
for curr_commit_idx in range(0, num_data, commit_size):
for i in range(curr_commit_idx, min(curr_commit_idx + commit_size, num_data)):
d, l = data[i], labels[i]
im_dat = caffe.io.array_to_datum(d.astype(float), label=int(l))
key = DB_KEY_FORMAT.format(curr_idx)
in_txn.put(key, im_dat.SerializeToString())
curr_idx += 1
db.close()
代码
with in_db_data.begin(write=True) as in_txn:
需要很多时间。
根据我的经验,我从 Python 将 Caffe 数据写入 Ubuntu 上的 ext4 硬盘上 50-100 毫秒写入 LMDB。 这就是为什么我使用 tmpfs(RAM 磁盘 功能内置于 Linux)并在 0.07 左右完成这些写入毫秒。你可以在你的 ramdisk 上制作更小的数据库,然后将它们复制到硬盘上,然后在所有这些数据库上进行训练。由于我有 64 GB 的 RAM,我正在制作大约 20-40GB 的内存。
一些代码可以帮助你们动态创建、填充 LMDB 并将其移动到存储中。随意编辑它以适合您的情况。它应该可以为您节省一些时间来了解 LMDB 和文件操作在 Python.
中的工作原理。import shutil
import lmdb
import random
def move_db():
global image_db
image_db.close();
rnd = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(5))
shutil.move( fold + 'ram/train_images', '/storage/lmdb/'+rnd)
open_db()
def open_db():
global image_db
image_db = lmdb.open(os.path.join(fold, 'ram/train_images'),
map_async=True,
max_dbs=0)
def write_to_lmdb(db, key, value):
"""
Write (key,value) to db
"""
success = False
while not success:
txn = db.begin(write=True)
try:
txn.put(key, value)
txn.commit()
success = True
except lmdb.MapFullError:
txn.abort()
# double the map_size
curr_limit = db.info()['map_size']
new_limit = curr_limit*2
print '>>> Doubling LMDB map size to %sMB ...' % (new_limit>>20,)
db.set_mapsize(new_limit) # double it
...
image_datum = caffe.io.array_to_datum( transformed_image, label )
write_to_lmdb(image_db, str(itr), image_datum.SerializeToString())
LMDB 写入对顺序非常敏感 - 如果您可以在插入之前对数据进行排序,速度将显着提高
我做了一个小基准来说明 Ophir 的观点:
机器:
RasPi 4B - 超频至 1.75 GHz,4GB,RasperryPi OS,SSD OS
代码:
def insert_lmdb(fsobj, transaction):
transaction.put(key=str(fsobj).encode("utf-8", "ignore"), value=generate_hash_from_file(fsobj).hexdigest().encode("utf-8", "ignore"))
list_f = list_files(FOLDER)
print(f"\n> Insert results in lmdb <")
list_f = Directory(path=DIR_ECTORY, use_hash=False, hash_from_content=False).lists["files"]
# list_f = sorted(list_f) # Run only in the 'sorted' case.
st = timeit.default_timer()
env = lmdb.open(path=DB_NAME)
with env.begin(write=True) as txn:
for i in list_f:
insert_lmdb(i, transaction=txn)
average = (timeit.default_timer() - st)*1000000/records
print(f"Test repeated {TIMES} times.\nNumber of files: {records}\nAverage time: {round(average, 3)} us or {round(1000000/average/1000, 3)}k inserts/sec")
结果:
不排序:[=37=]
> Insert results in lmdb <
Test repeated 50000 times.
Number of files: 363
Average time: 84 us or 12k inserts/sec
有排序:
> Insert results in lmdb <
Test repeated 50000 times.
Number of files: 363
Average time: 18.5 us or 54k inserts/sec
排序使写入速度提高了 4.5 倍,仅增加一行代码就不错了:)。