저는 파이썬에서 redis 캐시를 만들고 싶었습니다. 자존심이 강한 과학자처럼 벤치 마크를 통해 성능을 테스트했습니다.캐싱 응용 프로그램에서 Redis vs Disk의 성능
흥미롭게도, redis는 그렇게 잘하지 못했습니다. 파이썬이 마술 (파일 저장)을하거나, 나의 버전의 redis가 엄청나게 느리다.
이것이 내 코드가 구조화 된 방식 때문인지 또는 무엇을 알았는지는 알 수 없지만 이전보다 더 잘 할 수있을 것으로 기대하고있었습니다.
redis 캐시를 만들기 위해 이진 데이터 (이 경우 HTML 페이지)를 파일 이름에서 파생 된 키로 설정하고 만료일은 5 분입니다.
모든 경우 파일 처리는 f.read()로 수행됩니다 (이 작업은 f.readlines()보다 3 배 빠르며 이진 Blob이 필요합니다).
필자의 비교에서 누락 된 것이 있습니까? 아니면 Redis가 실제로 디스크와 일치하지 않습니까? 파이썬이 파일을 어딘가에 캐싱하고 매번 다시 액세스합니까? 왜 이것이 홍채에 접근하는 것보다 훨씬 빠릅니까?
저는 64 비트 우분투 시스템에서 모두 redis 2.8, python 2.7 및 redis-py를 사용하고 있습니다.
파이썬 객체에 파일 데이터를 저장하고 영원히 굴복시킨 함수를 만들었 기 때문에 파이썬이 특히 마술처럼 작용한다고 생각하지 않습니다.
나는 네 가지 기능은 내가 그룹화하는 것이 통화를 할 :
파일을 읽기 X 시간
레디 스 객체가 메모리에 남아있는 경우, 참조를로드하거나 (단일 새 파일을 캐시 호출되는 함수를 및 여러 개의 redis 인스턴스).
redis 데이터베이스의 결과를 생성하는 생성기를 생성하는 함수입니다 (단일 또는 다중 인스턴스의 redis).
마지막으로 파일을 메모리에 저장하고 영원히 산출합니다.
import redis
import time
def load_file(fp, fpKey, r, expiry):
with open(fp, "rb") as f:
data = f.read()
p = r.pipeline()
p.set(fpKey, data)
p.expire(fpKey, expiry)
p.execute()
return data
def cache_or_get_gen(fp, expiry=300, r=redis.Redis(db=5)):
fpKey = "cached:"+fp
while True:
yield load_file(fp, fpKey, r, expiry)
t = time.time()
while time.time() - t - expiry < 0:
yield r.get(fpKey)
def cache_or_get(fp, expiry=300, r=redis.Redis(db=5)):
fpKey = "cached:"+fp
if r.exists(fpKey):
return r.get(fpKey)
else:
with open(fp, "rb") as f:
data = f.read()
p = r.pipeline()
p.set(fpKey, data)
p.expire(fpKey, expiry)
p.execute()
return data
def mem_cache(fp):
with open(fp, "rb") as f:
data = f.readlines()
while True:
yield data
def stressTest(fp, trials = 10000):
# Read the file x number of times
a = time.time()
for x in range(trials):
with open(fp, "rb") as f:
data = f.read()
b = time.time()
readAvg = trials/(b-a)
# Generator version
# Read the file, cache it, read it with a new instance each time
a = time.time()
gen = cache_or_get_gen(fp)
for x in range(trials):
data = next(gen)
b = time.time()
cachedAvgGen = trials/(b-a)
# Read file, cache it, pass in redis instance each time
a = time.time()
r = redis.Redis(db=6)
gen = cache_or_get_gen(fp, r=r)
for x in range(trials):
data = next(gen)
b = time.time()
inCachedAvgGen = trials/(b-a)
# Non generator version
# Read the file, cache it, read it with a new instance each time
a = time.time()
for x in range(trials):
data = cache_or_get(fp)
b = time.time()
cachedAvg = trials/(b-a)
# Read file, cache it, pass in redis instance each time
a = time.time()
r = redis.Redis(db=6)
for x in range(trials):
data = cache_or_get(fp, r=r)
b = time.time()
inCachedAvg = trials/(b-a)
# Read file, cache it in python object
a = time.time()
for x in range(trials):
data = mem_cache(fp)
b = time.time()
memCachedAvg = trials/(b-a)
print "\n%s file reads: %.2f reads/second\n" %(trials, readAvg)
print "Yielding from generators for data:"
print "multi redis instance: %.2f reads/second (%.2f percent)" %(cachedAvgGen, (100*(cachedAvgGen-readAvg)/(readAvg)))
print "single redis instance: %.2f reads/second (%.2f percent)" %(inCachedAvgGen, (100*(inCachedAvgGen-readAvg)/(readAvg)))
print "Function calls to get data:"
print "multi redis instance: %.2f reads/second (%.2f percent)" %(cachedAvg, (100*(cachedAvg-readAvg)/(readAvg)))
print "single redis instance: %.2f reads/second (%.2f percent)" %(inCachedAvg, (100*(inCachedAvg-readAvg)/(readAvg)))
print "python cached object: %.2f reads/second (%.2f percent)" %(memCachedAvg, (100*(memCachedAvg-readAvg)/(readAvg)))
if __name__ == "__main__":
fileToRead = "templates/index.html"
stressTest(fileToRead)
이제 결과
10000 file reads: 30971.94 reads/second
Yielding from generators for data:
multi redis instance: 8489.28 reads/second (-72.59 percent)
single redis instance: 8801.73 reads/second (-71.58 percent)
Function calls to get data:
multi redis instance: 5396.81 reads/second (-82.58 percent)
single redis instance: 5419.19 reads/second (-82.50 percent)
python cached object: 1522765.03 reads/second (4816.60 percent)
결과 빨리 기능마다, b) 레디 스 디스크로부터 판독보다 느린 전화보다하는) 발전기의 흥미 및 c) 파이썬 객체에서 읽는 것은 엄청나게 빠릅니다.
왜 디스크에서 읽는 것이 redis에서 메모리 내 파일을 읽는 것보다 훨씬 빠릅니까?
편집 : 몇 가지 추가 정보 및 테스트.
나는, 결과는
if r.exists(fpKey):
data = r.get(fpKey)
Function calls to get data using r.exists as test
multi redis instance: 5320.51 reads/second (-82.34 percent)
single redis instance: 5308.33 reads/second (-82.38 percent)
python cached object: 1494123.68 reads/second (5348.17 percent)
Function calls to get data using if data as test
multi redis instance: 8540.91 reads/second (-71.25 percent)
single redis instance: 7888.24 reads/second (-73.45 percent)
python cached object: 1520226.17 reads/second (5132.01 percent)
실제로 눈에 띄는 읽기 속도에 영향이없는 각 함수 호출에 새로운 레디 스 인스턴스 작성에서 크게 다르지 않다
data = r.get(fpKey)
if data:
return r.get(fpKey)
에 기능을 대체 테스트에서 테스트까지의 변동성은 이득보다 큽니다.
Sripathi Krishnan은 임의 파일 읽기를 구현할 것을 제안했습니다. 이것이 캐싱이 실제로 도움이되는 곳입니다. 이러한 결과에서 알 수 있습니다.
Total number of files: 700
10000 file reads: 274.28 reads/second
Yielding from generators for data:
multi redis instance: 15393.30 reads/second (5512.32 percent)
single redis instance: 13228.62 reads/second (4723.09 percent)
Function calls to get data:
multi redis instance: 11213.54 reads/second (3988.40 percent)
single redis instance: 14420.15 reads/second (5157.52 percent)
python cached object: 607649.98 reads/second (221446.26 percent)
파일에 변화의 거대한 양 그래서 %의 차이는 속도 향상의 좋은 지표하지가 읽기입니다.
Total number of files: 700
40000 file reads: 1168.23 reads/second
Yielding from generators for data:
multi redis instance: 14900.80 reads/second (1175.50 percent)
single redis instance: 14318.28 reads/second (1125.64 percent)
Function calls to get data:
multi redis instance: 13563.36 reads/second (1061.02 percent)
single redis instance: 13486.05 reads/second (1054.40 percent)
python cached object: 587785.35 reads/second (50214.25 percent)
random.choice (fileList)를 사용하여 함수를 통과 할 때마다 임의로 새 파일을 선택했습니다.
전체 요점은 누군가가 그것을 밖으로 시도하려는 경우 여기에있다 - https://gist.github.com/3885957
편집 편집 : 내가 (발전기에 대해 하나 개의 단일 파일을 호출하는 것을 인식하지 않았지만 함수 호출 및 발전기의 성능 매우 유사했다). 다음은 생성기와 다른 파일의 결과입니다.
Total number of files: 700
10000 file reads: 284.48 reads/second
Yielding from generators for data:
single redis instance: 11627.56 reads/second (3987.36 percent)
Function calls to get data:
single redis instance: 14615.83 reads/second (5037.81 percent)
python cached object: 580285.56 reads/second (203884.21 percent)
모든 함수 호출에서 새 redis 인스턴스를 작성하는 위치가 표시되지 않습니다. 그것은 단지 기본 주장 일 이었습니까? – jdi
예, redis 인스턴스를 전달하지 않으면 함수 호출은 새 값을 가져옵니다. def cache_or_get (fp, expiry = 300, r = redis.Redis (db = 5)) : – MercuryRising
실제로는 사실이 아닙니다. 이러한 기본 인수는 스크립트가로드 될 때 한 번만 평가되고 함수 정의와 함께 저장됩니다. 전화 할 때마다 평가되지 않습니다. 그것은 왜 당신이 하나를 넘기는 것 사이에 어떤 차이를 보지 못했는지, 또는 그것들을 디폴트 값으로 사용하는 것의 차이를 보지 못했음을 설명 할 것입니다. 사실 당신이하고 있던 일은 각 함수 정의에 대해 하나씩 생성하고, 그것을 전달할 때마다 하나씩 생성하는 것입니다. 2 unused connections – jdi