如何优化代码并减少内存使用Python

6ie5vjzr  于 2022-11-19  发布在  Python
关注(0)|答案(1)|浏览(109)

其目的是减少内存使用。这意味着它应该以hash等于test hash的方式进行优化。
到目前为止,我已经尝试过:
1.添加了__slots__,但未进行任何更改。
1.将default dtypefloat64更改为float32。虽然这显著减少了mem的使用,但它通过更改哈希值来阻止测试。
1.将数据转换为np.array减少了CPU时间:from 13 s to 2.05 s,但不影响内存使用
要重现的代码:

rows = 40000000
trs = 10

random.seed(42)

generated_data: tp.List[float] = np.array([random.random() for _ in range(rows)])


def df_upd(df_initial: pd.DataFrame, df_new: pd.DataFrame) -> pd.DataFrame:
    return pd.concat((df_initial, df_new), axis=1)

class T:
    """adding a column of random data"""
    __slots__ = ['var']
    def __init__(self, var: float):
        self.var = var

    def transform(self, df_initial: pd.DataFrame) -> pd.DataFrame:
        return df_upd(df_initial, pd.DataFrame({self.var: generated_data}))

class Pipeline:
    __slots__ = ['df', 'transforms']
    def __init__(self):
        self.df = pd.DataFrame()
        self.transforms = np.array([T(f"v{i}") for i in range(trs)])

    def run(self):
        for t in self.transforms:
            self.df = t.transform(self.df)
        return self.df

if __name__ == "__main__":
    
    
    # starting the monitoring
    tracemalloc.start()

    # function call
    pipe = Pipeline()
    %time df = pipe.run()
    print("running")

    # displaying the memory
    current, peak = tracemalloc.get_traced_memory()
    print(f"Current memory usage is {current / 10**3} KB ({(current / 10**3)*0.001} MB); Peak was {peak / 10**3} KB ({(peak / 10**3)*0.001} MB); Diff = {(peak - current) / 10**3} KB ({((peak - current) / 10**3)*0.001} MB)")

    # stopping the library
    tracemalloc.stop()
    
    # should stay unchanged
    %time hashed_df = hashlib.sha256(pd.util.hash_pandas_object(df, index=True).values).hexdigest()
    print("hashed_df", hashed_df)    
    
    assert hashed_df == test_hash

    print("Success!")
zc0qhyus

zc0qhyus1#

如果您避免使用pd.concat()并使用 preferred 方式扩充 Dataframe :

df["new_col_name"] = new_col_data

这将显著降低峰值存储器消耗。
在代码中,修复Transform类就足够了:

class Transform:
    """adding a column of random data"""
    __slots__ = ['var']
    def __init__(self, var: str):
        self.var = var

    def transform(self, df: pd.DataFrame) -> pd.DataFrame:
        df[self.var] = generated_data
        return df

(Note我还将vartypefloat更改为str,以反映它在代码中的使用方式)。
在我的机器里,我从:
当前内存使用量为1600110.987 KB(1600.110987 MB);峰值为4480116.325KB(4480.116325MB);差异= 2880005.338 KB(2880.005338 MB)
至:
当前内存使用量为1760101.105 KB(1760.101105 MB);峰值为1760103.477KB(1760.1034769999999MB);差异= 2.372 KB(0.002372 MB)
(我不确定为什么在这种情况下当前的内存使用率略高)。
为了加快计算速度,您可能需要执行一些预分配。
为此,您可以在Pipeline的__init__()中替换:

self.df = pd.DataFrame()

与:

self.df = pd.DataFrame(data=np.empty((rows, trs)), columns=[f"v{i}" for i in range(trs)])

如果您希望更快,可以立即在管道的__init__中计算DataFrame,例如:

class Pipeline:
    __slots__ = ['df', 'transforms']
    def __init__(self):
        self.df = pd.DataFrame(data=generated_data[:, None] + np.zeros(trs)[None, :], columns=[f"v{i}" for i in range(trs)])

    def run(self):
        return self.df

但是我假设您的Transform是一个更复杂操作的代理,并且我不确定这种简化是否容易适应问题中的玩具代码。

相关问题